##// END OF EJS Templates
Merged with upstream.
gvaroquaux -
r1466:27953ac1 merge
parent child Browse files
Show More
@@ -0,0 +1,123 b''
1 # encoding: utf-8
2
3 """The IPython Core Notification Center.
4
5 See docs/source/development/notification_blueprint.txt for an overview of the
6 notification module.
7 """
8
9 __docformat__ = "restructuredtext en"
10
11 #-----------------------------------------------------------------------------
12 # Copyright (C) 2008 The IPython Development Team
13 #
14 # Distributed under the terms of the BSD License. The full license is in
15 # the file COPYING, distributed as part of this software.
16 #-----------------------------------------------------------------------------
17
18
19 class NotificationCenter(object):
20 """Synchronous notification center
21
22 Example
23 -------
24 >>> import IPython.kernel.core.notification as notification
25 >>> def callback(theType, theSender, args={}):
26 ... print theType,theSender,args
27 ...
28 >>> notification.sharedCenter.add_observer(callback, 'NOTIFICATION_TYPE', None)
29 >>> notification.sharedCenter.post_notification('NOTIFICATION_TYPE', object()) # doctest:+ELLIPSIS
30 NOTIFICATION_TYPE ...
31
32 """
33 def __init__(self):
34 super(NotificationCenter, self).__init__()
35 self._init_observers()
36
37
38 def _init_observers(self):
39 """Initialize observer storage"""
40
41 self.registered_types = set() #set of types that are observed
42 self.registered_senders = set() #set of senders that are observed
43 self.observers = {} #map (type,sender) => callback (callable)
44
45
46 def post_notification(self, theType, sender, **kwargs):
47 """Post notification (type,sender,**kwargs) to all registered
48 observers.
49
50 Implementation
51 --------------
52 * If no registered observers, performance is O(1).
53 * Notificaiton order is undefined.
54 * Notifications are posted synchronously.
55 """
56
57 if(theType==None or sender==None):
58 raise Exception("NotificationCenter.post_notification requires \
59 type and sender.")
60
61 # If there are no registered observers for the type/sender pair
62 if((theType not in self.registered_types and
63 None not in self.registered_types) or
64 (sender not in self.registered_senders and
65 None not in self.registered_senders)):
66 return
67
68 for o in self._observers_for_notification(theType, sender):
69 o(theType, sender, args=kwargs)
70
71
72 def _observers_for_notification(self, theType, sender):
73 """Find all registered observers that should recieve notification"""
74
75 keys = (
76 (theType,sender),
77 (theType, None),
78 (None, sender),
79 (None,None)
80 )
81
82
83 obs = set()
84 for k in keys:
85 obs.update(self.observers.get(k, set()))
86
87 return obs
88
89
90 def add_observer(self, callback, theType, sender):
91 """Add an observer callback to this notification center.
92
93 The given callback will be called upon posting of notifications of
94 the given type/sender and will receive any additional kwargs passed
95 to post_notification.
96
97 Parameters
98 ----------
99 observerCallback : callable
100 Callable. Must take at least two arguments::
101 observerCallback(type, sender, args={})
102
103 theType : hashable
104 The notification type. If None, all notifications from sender
105 will be posted.
106
107 sender : hashable
108 The notification sender. If None, all notifications of theType
109 will be posted.
110 """
111 assert(callback != None)
112 self.registered_types.add(theType)
113 self.registered_senders.add(sender)
114 self.observers.setdefault((theType,sender), set()).add(callback)
115
116 def remove_all_observers(self):
117 """Removes all observers from this notification center"""
118
119 self._init_observers()
120
121
122
123 sharedCenter = NotificationCenter() No newline at end of file
@@ -0,0 +1,171 b''
1 # encoding: utf-8
2
3 """This file contains unittests for the notification.py module."""
4
5 __docformat__ = "restructuredtext en"
6
7 #-----------------------------------------------------------------------------
8 # Copyright (C) 2008 The IPython Development Team
9 #
10 # Distributed under the terms of the BSD License. The full license is in
11 # the file COPYING, distributed as part of this software.
12 #-----------------------------------------------------------------------------
13
14 #-----------------------------------------------------------------------------
15 # Imports
16 #-----------------------------------------------------------------------------
17
18 import unittest
19 import IPython.kernel.core.notification as notification
20 from nose.tools import timed
21
22 #
23 # Supporting test classes
24 #
25
26 class Observer(object):
27 """docstring for Observer"""
28 def __init__(self, expectedType, expectedSender,
29 center=notification.sharedCenter, **kwargs):
30 super(Observer, self).__init__()
31 self.expectedType = expectedType
32 self.expectedSender = expectedSender
33 self.expectedKwArgs = kwargs
34 self.recieved = False
35 center.add_observer(self.callback,
36 self.expectedType,
37 self.expectedSender)
38
39
40 def callback(self, theType, sender, args={}):
41 """callback"""
42
43 assert(theType == self.expectedType or
44 self.expectedType == None)
45 assert(sender == self.expectedSender or
46 self.expectedSender == None)
47 assert(args == self.expectedKwArgs)
48 self.recieved = True
49
50
51 def verify(self):
52 """verify"""
53
54 assert(self.recieved)
55
56 def reset(self):
57 """reset"""
58
59 self.recieved = False
60
61
62
63 class Notifier(object):
64 """docstring for Notifier"""
65 def __init__(self, theType, **kwargs):
66 super(Notifier, self).__init__()
67 self.theType = theType
68 self.kwargs = kwargs
69
70 def post(self, center=notification.sharedCenter):
71 """fire"""
72
73 center.post_notification(self.theType, self,
74 **self.kwargs)
75
76
77 #
78 # Test Cases
79 #
80
81 class NotificationTests(unittest.TestCase):
82 """docstring for NotificationTests"""
83
84 def tearDown(self):
85 notification.sharedCenter.remove_all_observers()
86
87 def test_notification_delivered(self):
88 """Test that notifications are delivered"""
89 expectedType = 'EXPECTED_TYPE'
90 sender = Notifier(expectedType)
91 observer = Observer(expectedType, sender)
92
93 sender.post()
94
95 observer.verify()
96
97
98 def test_type_specificity(self):
99 """Test that observers are registered by type"""
100
101 expectedType = 1
102 unexpectedType = "UNEXPECTED_TYPE"
103 sender = Notifier(expectedType)
104 unexpectedSender = Notifier(unexpectedType)
105 observer = Observer(expectedType, sender)
106
107 sender.post()
108 unexpectedSender.post()
109
110 observer.verify()
111
112
113 def test_sender_specificity(self):
114 """Test that observers are registered by sender"""
115
116 expectedType = "EXPECTED_TYPE"
117 sender1 = Notifier(expectedType)
118 sender2 = Notifier(expectedType)
119 observer = Observer(expectedType, sender1)
120
121 sender1.post()
122 sender2.post()
123
124 observer.verify()
125
126
127 def test_remove_all_observers(self):
128 """White-box test for remove_all_observers"""
129
130 for i in xrange(10):
131 Observer('TYPE', None, center=notification.sharedCenter)
132
133 self.assert_(len(notification.sharedCenter.observers[('TYPE',None)]) >= 10,
134 "observers registered")
135
136 notification.sharedCenter.remove_all_observers()
137
138 self.assert_(len(notification.sharedCenter.observers) == 0, "observers removed")
139
140
141 def test_any_sender(self):
142 """test_any_sender"""
143
144 expectedType = "EXPECTED_TYPE"
145 sender1 = Notifier(expectedType)
146 sender2 = Notifier(expectedType)
147 observer = Observer(expectedType, None)
148
149
150 sender1.post()
151 observer.verify()
152
153 observer.reset()
154 sender2.post()
155 observer.verify()
156
157
158 @timed(.01)
159 def test_post_performance(self):
160 """Test that post_notification, even with many registered irrelevant
161 observers is fast"""
162
163 for i in xrange(10):
164 Observer("UNRELATED_TYPE", None)
165
166 o = Observer('EXPECTED_TYPE', None)
167
168 notification.sharedCenter.post_notification('EXPECTED_TYPE', self)
169
170 o.verify()
171
@@ -0,0 +1,233 b''
1 # encoding: utf-8
2
3 """A parallelized version of Python's builtin map."""
4
5 __docformat__ = "restructuredtext en"
6
7 #----------------------------------------------------------------------------
8 # Copyright (C) 2008 The IPython Development Team
9 #
10 # Distributed under the terms of the BSD License. The full license is in
11 # the file COPYING, distributed as part of this software.
12 #----------------------------------------------------------------------------
13
14 #----------------------------------------------------------------------------
15 # Imports
16 #----------------------------------------------------------------------------
17
18 from types import FunctionType
19 from zope.interface import Interface, implements
20 from IPython.kernel.task import MapTask
21 from IPython.kernel.twistedutil import DeferredList, gatherBoth
22 from IPython.kernel.util import printer
23 from IPython.kernel.error import collect_exceptions
24
25 #----------------------------------------------------------------------------
26 # Code
27 #----------------------------------------------------------------------------
28
29 class IMapper(Interface):
30 """The basic interface for a Mapper.
31
32 This defines a generic interface for mapping. The idea of this is
33 similar to that of Python's builtin `map` function, which applies a function
34 elementwise to a sequence.
35 """
36
37 def map(func, *seqs):
38 """Do map in parallel.
39
40 Equivalent to map(func, *seqs) or:
41
42 [func(seqs[0][0], seqs[1][0],...), func(seqs[0][1], seqs[1][1],...),...]
43
44 :Parameters:
45 func : FunctionType
46 The function to apply to the sequence
47 sequences : tuple of iterables
48 A sequence of iterables that are used for sucessive function
49 arguments. This work just like map
50 """
51
52 class IMultiEngineMapperFactory(Interface):
53 """
54 An interface for something that creates `IMapper` instances.
55 """
56
57 def mapper(dist='b', targets='all', block=True):
58 """
59 Create an `IMapper` implementer with a given set of arguments.
60
61 The `IMapper` created using a multiengine controller is
62 not load balanced.
63 """
64
65 class ITaskMapperFactory(Interface):
66 """
67 An interface for something that creates `IMapper` instances.
68 """
69
70 def mapper(clear_before=False, clear_after=False, retries=0,
71 recovery_task=None, depend=None, block=True):
72 """
73 Create an `IMapper` implementer with a given set of arguments.
74
75 The `IMapper` created using a task controller is load balanced.
76
77 See the documentation for `IPython.kernel.task.BaseTask` for
78 documentation on the arguments to this method.
79 """
80
81
82 class MultiEngineMapper(object):
83 """
84 A Mapper for `IMultiEngine` implementers.
85 """
86
87 implements(IMapper)
88
89 def __init__(self, multiengine, dist='b', targets='all', block=True):
90 """
91 Create a Mapper for a multiengine.
92
93 The value of all arguments are used for all calls to `map`. This
94 class allows these arguemnts to be set for a series of map calls.
95
96 :Parameters:
97 multiengine : `IMultiEngine` implementer
98 The multiengine to use for running the map commands
99 dist : str
100 The type of decomposition to use. Only block ('b') is
101 supported currently
102 targets : (str, int, tuple of ints)
103 The engines to use in the map
104 block : boolean
105 Whether to block when the map is applied
106 """
107 self.multiengine = multiengine
108 self.dist = dist
109 self.targets = targets
110 self.block = block
111
112 def map(self, func, *sequences):
113 """
114 Apply func to *sequences elementwise. Like Python's builtin map.
115
116 This version is not load balanced.
117 """
118 max_len = max(len(s) for s in sequences)
119 for s in sequences:
120 if len(s)!=max_len:
121 raise ValueError('all sequences must have equal length')
122 assert isinstance(func, (str, FunctionType)), "func must be a fuction or str"
123 return self.multiengine.raw_map(func, sequences, dist=self.dist,
124 targets=self.targets, block=self.block)
125
126 class TaskMapper(object):
127 """
128 Make an `ITaskController` look like an `IMapper`.
129
130 This class provides a load balanced version of `map`.
131 """
132
133 def __init__(self, task_controller, clear_before=False, clear_after=False, retries=0,
134 recovery_task=None, depend=None, block=True):
135 """
136 Create a `IMapper` given a `TaskController` and arguments.
137
138 The additional arguments are those that are common to all types of
139 tasks and are described in the documentation for
140 `IPython.kernel.task.BaseTask`.
141
142 :Parameters:
143 task_controller : an `IBlockingTaskClient` implementer
144 The `TaskController` to use for calls to `map`
145 """
146 self.task_controller = task_controller
147 self.clear_before = clear_before
148 self.clear_after = clear_after
149 self.retries = retries
150 self.recovery_task = recovery_task
151 self.depend = depend
152 self.block = block
153
154 def map(self, func, *sequences):
155 """
156 Apply func to *sequences elementwise. Like Python's builtin map.
157
158 This version is load balanced.
159 """
160 max_len = max(len(s) for s in sequences)
161 for s in sequences:
162 if len(s)!=max_len:
163 raise ValueError('all sequences must have equal length')
164 task_args = zip(*sequences)
165 task_ids = []
166 dlist = []
167 for ta in task_args:
168 task = MapTask(func, ta, clear_before=self.clear_before,
169 clear_after=self.clear_after, retries=self.retries,
170 recovery_task=self.recovery_task, depend=self.depend)
171 dlist.append(self.task_controller.run(task))
172 dlist = gatherBoth(dlist, consumeErrors=1)
173 dlist.addCallback(collect_exceptions,'map')
174 if self.block:
175 def get_results(task_ids):
176 d = self.task_controller.barrier(task_ids)
177 d.addCallback(lambda _: gatherBoth([self.task_controller.get_task_result(tid) for tid in task_ids], consumeErrors=1))
178 d.addCallback(collect_exceptions, 'map')
179 return d
180 dlist.addCallback(get_results)
181 return dlist
182
183 class SynchronousTaskMapper(object):
184 """
185 Make an `IBlockingTaskClient` look like an `IMapper`.
186
187 This class provides a load balanced version of `map`.
188 """
189
190 def __init__(self, task_controller, clear_before=False, clear_after=False, retries=0,
191 recovery_task=None, depend=None, block=True):
192 """
193 Create a `IMapper` given a `IBlockingTaskClient` and arguments.
194
195 The additional arguments are those that are common to all types of
196 tasks and are described in the documentation for
197 `IPython.kernel.task.BaseTask`.
198
199 :Parameters:
200 task_controller : an `IBlockingTaskClient` implementer
201 The `TaskController` to use for calls to `map`
202 """
203 self.task_controller = task_controller
204 self.clear_before = clear_before
205 self.clear_after = clear_after
206 self.retries = retries
207 self.recovery_task = recovery_task
208 self.depend = depend
209 self.block = block
210
211 def map(self, func, *sequences):
212 """
213 Apply func to *sequences elementwise. Like Python's builtin map.
214
215 This version is load balanced.
216 """
217 max_len = max(len(s) for s in sequences)
218 for s in sequences:
219 if len(s)!=max_len:
220 raise ValueError('all sequences must have equal length')
221 task_args = zip(*sequences)
222 task_ids = []
223 for ta in task_args:
224 task = MapTask(func, ta, clear_before=self.clear_before,
225 clear_after=self.clear_after, retries=self.retries,
226 recovery_task=self.recovery_task, depend=self.depend)
227 task_ids.append(self.task_controller.run(task))
228 if self.block:
229 self.task_controller.barrier(task_ids)
230 task_results = [self.task_controller.get_task_result(tid) for tid in task_ids]
231 return task_results
232 else:
233 return task_ids No newline at end of file
@@ -0,0 +1,41 b''
1 from __future__ import with_statement
2
3 #def test_simple():
4 if 0:
5
6 # XXX - for now, we need a running cluster to be started separately. The
7 # daemon work is almost finished, and will make much of this unnecessary.
8 from IPython.kernel import client
9 mec = client.MultiEngineClient(('127.0.0.1',10105))
10
11 try:
12 mec.get_ids()
13 except ConnectionRefusedError:
14 import os, time
15 os.system('ipcluster -n 2 &')
16 time.sleep(2)
17 mec = client.MultiEngineClient(('127.0.0.1',10105))
18
19 mec.block = False
20
21 import itertools
22 c = itertools.count()
23
24 parallel = RemoteMultiEngine(mec)
25
26 mec.pushAll()
27
28 with parallel as pr:
29 # A comment
30 remote() # this means the code below only runs remotely
31 print 'Hello remote world'
32 x = range(10)
33 # Comments are OK
34 # Even misindented.
35 y = x+1
36
37
38 with pfor('i',sequence) as pr:
39 print x[i]
40
41 print pr.x + pr.y
@@ -0,0 +1,146 b''
1 ## The basic trick is to generate the source code for the decorated function
2 ## with the right signature and to evaluate it.
3 ## Uncomment the statement 'print >> sys.stderr, func_src' in _decorate
4 ## to understand what is going on.
5
6 __all__ = ["decorator", "update_wrapper", "getinfo"]
7
8 import inspect, sys
9
10 def getinfo(func):
11 """
12 Returns an info dictionary containing:
13 - name (the name of the function : str)
14 - argnames (the names of the arguments : list)
15 - defaults (the values of the default arguments : tuple)
16 - signature (the signature : str)
17 - doc (the docstring : str)
18 - module (the module name : str)
19 - dict (the function __dict__ : str)
20
21 >>> def f(self, x=1, y=2, *args, **kw): pass
22
23 >>> info = getinfo(f)
24
25 >>> info["name"]
26 'f'
27 >>> info["argnames"]
28 ['self', 'x', 'y', 'args', 'kw']
29
30 >>> info["defaults"]
31 (1, 2)
32
33 >>> info["signature"]
34 'self, x, y, *args, **kw'
35 """
36 assert inspect.ismethod(func) or inspect.isfunction(func)
37 regargs, varargs, varkwargs, defaults = inspect.getargspec(func)
38 argnames = list(regargs)
39 if varargs:
40 argnames.append(varargs)
41 if varkwargs:
42 argnames.append(varkwargs)
43 signature = inspect.formatargspec(regargs, varargs, varkwargs, defaults,
44 formatvalue=lambda value: "")[1:-1]
45 return dict(name=func.__name__, argnames=argnames, signature=signature,
46 defaults = func.func_defaults, doc=func.__doc__,
47 module=func.__module__, dict=func.__dict__,
48 globals=func.func_globals, closure=func.func_closure)
49
50 def update_wrapper(wrapper, wrapped, create=False):
51 """
52 An improvement over functools.update_wrapper. By default it works the
53 same, but if the 'create' flag is set, generates a copy of the wrapper
54 with the right signature and update the copy, not the original.
55 Moreovoer, 'wrapped' can be a dictionary with keys 'name', 'doc', 'module',
56 'dict', 'defaults'.
57 """
58 if isinstance(wrapped, dict):
59 infodict = wrapped
60 else: # assume wrapped is a function
61 infodict = getinfo(wrapped)
62 assert not '_wrapper_' in infodict["argnames"], \
63 '"_wrapper_" is a reserved argument name!'
64 if create: # create a brand new wrapper with the right signature
65 src = "lambda %(signature)s: _wrapper_(%(signature)s)" % infodict
66 # import sys; print >> sys.stderr, src # for debugging purposes
67 wrapper = eval(src, dict(_wrapper_=wrapper))
68 try:
69 wrapper.__name__ = infodict['name']
70 except: # Python version < 2.4
71 pass
72 wrapper.__doc__ = infodict['doc']
73 wrapper.__module__ = infodict['module']
74 wrapper.__dict__.update(infodict['dict'])
75 wrapper.func_defaults = infodict['defaults']
76 return wrapper
77
78 # the real meat is here
79 def _decorator(caller, func):
80 infodict = getinfo(func)
81 argnames = infodict['argnames']
82 assert not ('_call_' in argnames or '_func_' in argnames), \
83 'You cannot use _call_ or _func_ as argument names!'
84 src = "lambda %(signature)s: _call_(_func_, %(signature)s)" % infodict
85 dec_func = eval(src, dict(_func_=func, _call_=caller))
86 return update_wrapper(dec_func, func)
87
88 def decorator(caller, func=None):
89 """
90 General purpose decorator factory: takes a caller function as
91 input and returns a decorator with the same attributes.
92 A caller function is any function like this::
93
94 def caller(func, *args, **kw):
95 # do something
96 return func(*args, **kw)
97
98 Here is an example of usage:
99
100 >>> @decorator
101 ... def chatty(f, *args, **kw):
102 ... print "Calling %r" % f.__name__
103 ... return f(*args, **kw)
104
105 >>> chatty.__name__
106 'chatty'
107
108 >>> @chatty
109 ... def f(): pass
110 ...
111 >>> f()
112 Calling 'f'
113
114 For sake of convenience, the decorator factory can also be called with
115 two arguments. In this casem ``decorator(caller, func)`` is just a
116 shortcut for ``decorator(caller)(func)``.
117 """
118 if func is None: # return a decorator function
119 return update_wrapper(lambda f : _decorator(caller, f), caller)
120 else: # return a decorated function
121 return _decorator(caller, func)
122
123 if __name__ == "__main__":
124 import doctest; doctest.testmod()
125
126 ####################### LEGALESE ##################################
127
128 ## Redistributions of source code must retain the above copyright
129 ## notice, this list of conditions and the following disclaimer.
130 ## Redistributions in bytecode form must reproduce the above copyright
131 ## notice, this list of conditions and the following disclaimer in
132 ## the documentation and/or other materials provided with the
133 ## distribution.
134
135 ## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
136 ## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
137 ## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
138 ## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
139 ## HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
140 ## INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
141 ## BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
142 ## OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
143 ## ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
144 ## TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
145 ## USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
146 ## DAMAGE.
@@ -0,0 +1,133 b''
1 """Decorators for labeling test objects.
2
3 Decorators that merely return a modified version of the original
4 function object are straightforward. Decorators that return a new
5 function object need to use
6 nose.tools.make_decorator(original_function)(decorator) in returning
7 the decorator, in order to preserve metadata such as function name,
8 setup and teardown functions and so on - see nose.tools for more
9 information.
10
11 NOTE: This file contains IPython-specific decorators and imports the
12 numpy.testing.decorators file, which we've copied verbatim. Any of our own
13 code will be added at the bottom if we end up extending this.
14 """
15
16 # Stdlib imports
17 import inspect
18
19 # Third-party imports
20
21 # This is Michele Simionato's decorator module, also kept verbatim.
22 from decorator_msim import decorator, update_wrapper
23
24 # Grab the numpy-specific decorators which we keep in a file that we
25 # occasionally update from upstream: decorators_numpy.py is an IDENTICAL copy
26 # of numpy.testing.decorators.
27 from decorators_numpy import *
28
29 ##############################################################################
30 # Local code begins
31
32 # Utility functions
33
34 def apply_wrapper(wrapper,func):
35 """Apply a wrapper to a function for decoration.
36
37 This mixes Michele Simionato's decorator tool with nose's make_decorator,
38 to apply a wrapper in a decorator so that all nose attributes, as well as
39 function signature and other properties, survive the decoration cleanly.
40 This will ensure that wrapped functions can still be well introspected via
41 IPython, for example.
42 """
43 import nose.tools
44
45 return decorator(wrapper,nose.tools.make_decorator(func)(wrapper))
46
47
48 def make_label_dec(label,ds=None):
49 """Factory function to create a decorator that applies one or more labels.
50
51 :Parameters:
52 label : string or sequence
53 One or more labels that will be applied by the decorator to the functions
54 it decorates. Labels are attributes of the decorated function with their
55 value set to True.
56
57 :Keywords:
58 ds : string
59 An optional docstring for the resulting decorator. If not given, a
60 default docstring is auto-generated.
61
62 :Returns:
63 A decorator.
64
65 :Examples:
66
67 A simple labeling decorator:
68 >>> slow = make_label_dec('slow')
69 >>> print slow.__doc__
70 Labels a test as 'slow'.
71
72 And one that uses multiple labels and a custom docstring:
73 >>> rare = make_label_dec(['slow','hard'],
74 ... "Mix labels 'slow' and 'hard' for rare tests.")
75 >>> print rare.__doc__
76 Mix labels 'slow' and 'hard' for rare tests.
77
78 Now, let's test using this one:
79 >>> @rare
80 ... def f(): pass
81 ...
82 >>>
83 >>> f.slow
84 True
85 >>> f.hard
86 True
87 """
88
89 if isinstance(label,basestring):
90 labels = [label]
91 else:
92 labels = label
93
94 # Validate that the given label(s) are OK for use in setattr() by doing a
95 # dry run on a dummy function.
96 tmp = lambda : None
97 for label in labels:
98 setattr(tmp,label,True)
99
100 # This is the actual decorator we'll return
101 def decor(f):
102 for label in labels:
103 setattr(f,label,True)
104 return f
105
106 # Apply the user's docstring, or autogenerate a basic one
107 if ds is None:
108 ds = "Labels a test as %r." % label
109 decor.__doc__ = ds
110
111 return decor
112
113 #-----------------------------------------------------------------------------
114 # Decorators for public use
115
116 skip_doctest = make_label_dec('skip_doctest',
117 """Decorator - mark a function or method for skipping its doctest.
118
119 This decorator allows you to mark a function whose docstring you wish to
120 omit from testing, while preserving the docstring for introspection, help,
121 etc.""")
122
123
124 def skip(func):
125 """Decorator - mark a test function for skipping from test suite."""
126
127 import nose
128
129 def wrapper(*a,**k):
130 raise nose.SkipTest("Skipping test for function: %s" %
131 func.__name__)
132
133 return apply_wrapper(wrapper,func)
@@ -0,0 +1,94 b''
1 """Decorators for labeling test objects
2
3 Decorators that merely return a modified version of the original
4 function object are straightforward. Decorators that return a new
5 function object need to use
6 nose.tools.make_decorator(original_function)(decorator) in returning
7 the decorator, in order to preserve metadata such as function name,
8 setup and teardown functions and so on - see nose.tools for more
9 information.
10
11 """
12
13 def slow(t):
14 """Labels a test as 'slow'.
15
16 The exact definition of a slow test is obviously both subjective and
17 hardware-dependent, but in general any individual test that requires more
18 than a second or two should be labeled as slow (the whole suite consits of
19 thousands of tests, so even a second is significant)."""
20
21 t.slow = True
22 return t
23
24 def setastest(tf=True):
25 ''' Signals to nose that this function is or is not a test
26
27 Parameters
28 ----------
29 tf : bool
30 If True specifies this is a test, not a test otherwise
31
32 e.g
33 >>> from numpy.testing.decorators import setastest
34 >>> @setastest(False)
35 ... def func_with_test_in_name(arg1, arg2): pass
36 ...
37 >>>
38
39 This decorator cannot use the nose namespace, because it can be
40 called from a non-test module. See also istest and nottest in
41 nose.tools
42
43 '''
44 def set_test(t):
45 t.__test__ = tf
46 return t
47 return set_test
48
49 def skipif(skip_condition, msg=None):
50 ''' Make function raise SkipTest exception if skip_condition is true
51
52 Parameters
53 ---------
54 skip_condition : bool
55 Flag to determine whether to skip test (True) or not (False)
56 msg : string
57 Message to give on raising a SkipTest exception
58
59 Returns
60 -------
61 decorator : function
62 Decorator, which, when applied to a function, causes SkipTest
63 to be raised when the skip_condition was True, and the function
64 to be called normally otherwise.
65
66 Notes
67 -----
68 You will see from the code that we had to further decorate the
69 decorator with the nose.tools.make_decorator function in order to
70 transmit function name, and various other metadata.
71 '''
72 if msg is None:
73 msg = 'Test skipped due to test condition'
74 def skip_decorator(f):
75 # Local import to avoid a hard nose dependency and only incur the
76 # import time overhead at actual test-time.
77 import nose
78 def skipper(*args, **kwargs):
79 if skip_condition:
80 raise nose.SkipTest, msg
81 else:
82 return f(*args, **kwargs)
83 return nose.tools.make_decorator(f)(skipper)
84 return skip_decorator
85
86 def skipknownfailure(f):
87 ''' Decorator to raise SkipTest for test known to fail
88 '''
89 # Local import to avoid a hard nose dependency and only incur the
90 # import time overhead at actual test-time.
91 import nose
92 def skipper(*args, **kwargs):
93 raise nose.SkipTest, 'This test is known to fail'
94 return nose.tools.make_decorator(f)(skipper)
@@ -0,0 +1,17 b''
1 """Simple script to show reference holding behavior.
2
3 This is used by a companion test case.
4 """
5
6 import gc
7
8 class C(object):
9 def __del__(self):
10 print 'deleting object...'
11
12 c = C()
13
14 c_refs = gc.get_referrers(c)
15 ref_ids = map(id,c_refs)
16
17 print 'c referrers:',map(type,c_refs)
@@ -0,0 +1,2 b''
1 x = 1
2 print 'x is:',x
@@ -0,0 +1,180 b''
1 # Module imports
2 # Std lib
3 import inspect
4
5 # Third party
6
7 # Our own
8 from IPython.testing import decorators as dec
9
10 #-----------------------------------------------------------------------------
11 # Utilities
12
13 # Note: copied from OInspect, kept here so the testing stuff doesn't create
14 # circular dependencies and is easier to reuse.
15 def getargspec(obj):
16 """Get the names and default values of a function's arguments.
17
18 A tuple of four things is returned: (args, varargs, varkw, defaults).
19 'args' is a list of the argument names (it may contain nested lists).
20 'varargs' and 'varkw' are the names of the * and ** arguments or None.
21 'defaults' is an n-tuple of the default values of the last n arguments.
22
23 Modified version of inspect.getargspec from the Python Standard
24 Library."""
25
26 if inspect.isfunction(obj):
27 func_obj = obj
28 elif inspect.ismethod(obj):
29 func_obj = obj.im_func
30 else:
31 raise TypeError, 'arg is not a Python function'
32 args, varargs, varkw = inspect.getargs(func_obj.func_code)
33 return args, varargs, varkw, func_obj.func_defaults
34
35 #-----------------------------------------------------------------------------
36 # Testing functions
37
38 def test_trivial():
39 """A trivial passing test."""
40 pass
41
42
43 @dec.skip
44 def test_deliberately_broken():
45 """A deliberately broken test - we want to skip this one."""
46 1/0
47
48
49 # Verify that we can correctly skip the doctest for a function at will, but
50 # that the docstring itself is NOT destroyed by the decorator.
51 @dec.skip_doctest
52 def doctest_bad(x,y=1,**k):
53 """A function whose doctest we need to skip.
54
55 >>> 1+1
56 3
57 """
58 print 'x:',x
59 print 'y:',y
60 print 'k:',k
61
62
63 def call_doctest_bad():
64 """Check that we can still call the decorated functions.
65
66 >>> doctest_bad(3,y=4)
67 x: 3
68 y: 4
69 k: {}
70 """
71 pass
72
73
74 # Doctest skipping should work for class methods too
75 class foo(object):
76 """Foo
77
78 Example:
79
80 >>> 1+1
81 2
82 """
83
84 @dec.skip_doctest
85 def __init__(self,x):
86 """Make a foo.
87
88 Example:
89
90 >>> f = foo(3)
91 junk
92 """
93 print 'Making a foo.'
94 self.x = x
95
96 @dec.skip_doctest
97 def bar(self,y):
98 """Example:
99
100 >>> f = foo(3)
101 >>> f.bar(0)
102 boom!
103 >>> 1/0
104 bam!
105 """
106 return 1/y
107
108 def baz(self,y):
109 """Example:
110
111 >>> f = foo(3)
112 Making a foo.
113 >>> f.baz(3)
114 True
115 """
116 return self.x==y
117
118
119 def test_skip_dt_decorator():
120 """Doctest-skipping decorator should preserve the docstring.
121 """
122 # Careful: 'check' must be a *verbatim* copy of the doctest_bad docstring!
123 check = """A function whose doctest we need to skip.
124
125 >>> 1+1
126 3
127 """
128 # Fetch the docstring from doctest_bad after decoration.
129 val = doctest_bad.__doc__
130
131 assert check==val,"doctest_bad docstrings don't match"
132
133
134 def test_skip_dt_decorator2():
135 """Doctest-skipping decorator should preserve function signature.
136 """
137 # Hardcoded correct answer
138 dtargs = (['x', 'y'], None, 'k', (1,))
139 # Introspect out the value
140 dtargsr = getargspec(doctest_bad)
141 assert dtargsr==dtargs, \
142 "Incorrectly reconstructed args for doctest_bad: %s" % (dtargsr,)
143
144
145 def doctest_run():
146 """Test running a trivial script.
147
148 In [13]: run simplevars.py
149 x is: 1
150 """
151
152 #@dec.skip_doctest
153 def doctest_runvars():
154 """Test that variables defined in scripts get loaded correcly via %run.
155
156 In [13]: run simplevars.py
157 x is: 1
158
159 In [14]: x
160 Out[14]: 1
161 """
162
163 def doctest_ivars():
164 """Test that variables defined interactively are picked up.
165 In [5]: zz=1
166
167 In [6]: zz
168 Out[6]: 1
169 """
170
171 @dec.skip_doctest
172 def doctest_refs():
173 """DocTest reference holding issues when running scripts.
174
175 In [32]: run show_refs.py
176 c referrers: [<type 'dict'>]
177
178 In [33]: map(type,gc.get_referrers(c))
179 Out[33]: [<type 'dict'>]
180 """
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
@@ -0,0 +1,18 b''
1 from IPython.kernel import client
2
3 mec = client.MultiEngineClient()
4
5 result = mec.map(lambda x: 2*x, range(10))
6 print "Simple, default map: ", result
7
8 m = mec.mapper(block=False)
9 pr = m.map(lambda x: 2*x, range(10))
10 print "Submitted map, got PendingResult: ", pr
11 result = pr.r
12 print "Using a mapper: ", result
13
14 @mec.parallel()
15 def f(x): return 2*x
16
17 result = f(range(10))
18 print "Using a parallel function: ", result No newline at end of file
@@ -0,0 +1,19 b''
1 from IPython.kernel import client
2
3 tc = client.TaskClient()
4
5 result = tc.map(lambda x: 2*x, range(10))
6 print "Simple, default map: ", result
7
8 m = tc.mapper(block=False, clear_after=True, clear_before=True)
9 tids = m.map(lambda x: 2*x, range(10))
10 print "Submitted tasks, got ids: ", tids
11 tc.barrier(tids)
12 result = [tc.get_task_result(tid) for tid in tids]
13 print "Using a mapper: ", result
14
15 @tc.parallel()
16 def f(x): return 2*x
17
18 result = f(range(10))
19 print "Using a parallel function: ", result No newline at end of file
@@ -0,0 +1,47 b''
1 .. Notification:
2
3 ==========================================
4 IPython.kernel.core.notification blueprint
5 ==========================================
6
7 Overview
8 ========
9 The :mod:`IPython.kernel.core.notification` module will provide a simple implementation of a notification center and support for the observer pattern within the :mod:`IPython.kernel.core`. The main intended use case is to provide notification of Interpreter events to an observing frontend during the execution of a single block of code.
10
11 Functional Requirements
12 =======================
13 The notification center must:
14 * Provide synchronous notification of events to all registered observers.
15 * Provide typed or labeled notification types
16 * Allow observers to register callbacks for individual or all notification types
17 * Allow observers to register callbacks for events from individual or all notifying objects
18 * Notification to the observer consists of the notification type, notifying object and user-supplied extra information [implementation: as keyword parameters to the registered callback]
19 * Perform as O(1) in the case of no registered observers.
20 * Permit out-of-process or cross-network extension.
21
22 What's not included
23 ==============================================================
24 As written, the :mod:`IPython.kernel.core.notificaiton` module does not:
25 * Provide out-of-process or network notifications [these should be handled by a separate, Twisted aware module in :mod:`IPython.kernel`].
26 * Provide zope.interface-style interfaces for the notification system [these should also be provided by the :mod:`IPython.kernel` module]
27
28 Use Cases
29 =========
30 The following use cases describe the main intended uses of the notificaiton module and illustrate the main success scenario for each use case:
31
32 1. Dwight Schroot is writing a frontend for the IPython project. His frontend is stuck in the stone age and must communicate synchronously with an IPython.kernel.core.Interpreter instance. Because code is executed in blocks by the Interpreter, Dwight's UI freezes every time he executes a long block of code. To keep track of the progress of his long running block, Dwight adds the following code to his frontend's set-up code::
33 from IPython.kernel.core.notification import NotificationCenter
34 center = NotificationCenter.sharedNotificationCenter
35 center.registerObserver(self, type=IPython.kernel.core.Interpreter.STDOUT_NOTIFICATION_TYPE, notifying_object=self.interpreter, callback=self.stdout_notification)
36
37 and elsewhere in his front end::
38 def stdout_notification(self, type, notifying_object, out_string=None):
39 self.writeStdOut(out_string)
40
41 If everything works, the Interpreter will (according to its published API) fire a notification via the :data:`IPython.kernel.core.notification.sharedCenter` of type :const:`STD_OUT_NOTIFICATION_TYPE` before writing anything to stdout [it's up to the Intereter implementation to figure out when to do this]. The notificaiton center will then call the registered callbacks for that event type (in this case, Dwight's frontend's stdout_notification method). Again, according to its API, the Interpreter provides an additional keyword argument when firing the notificaiton of out_string, a copy of the string it will write to stdout.
42
43 Like magic, Dwight's frontend is able to provide output, even during long-running calculations. Now if Jim could just convince Dwight to use Twisted...
44
45 2. Boss Hog is writing a frontend for the IPython project. Because Boss Hog is stuck in the stone age, his frontend will be written in a new Fortran-like dialect of python and will run only from the command line. Because he doesn't need any fancy notification system and is used to worrying about every cycle on his rat-wheel powered mini, Boss Hog is adamant that the new notification system not produce any performance penalty. As they say in Hazard county, there's no such thing as a free lunch. If he wanted zero overhead, he should have kept using IPython 0.8. Instead, those tricky Duke boys slide in a suped-up bridge-out jumpin' awkwardly confederate-lovin' notification module that imparts only a constant (and small) performance penalty when the Interpreter (or any other object) fires an event for which there are no registered observers. Of course, the same notificaiton-enabled Interpreter can then be used in frontends that require notifications, thus saving the IPython project from a nasty civil war.
46
47 3. Barry is wrting a frontend for the IPython project. Because Barry's front end is the *new hotness*, it uses an asynchronous event model to communicate with a Twisted :mod:`~IPython.kernel.engineservice` that communicates with the IPython :class:`~IPython.kernel.core.interpreter.Interpreter`. Using the :mod:`IPython.kernel.notification` module, an asynchronous wrapper on the :mod:`IPython.kernel.core.notification` module, Barry's frontend can register for notifications from the interpreter that are delivered asynchronously. Even if Barry's frontend is running on a separate process or even host from the Interpreter, the notifications are delivered, as if by dark and twisted magic. Just like Dwight's frontend, Barry's frontend can now recieve notifications of e.g. writing to stdout/stderr, opening/closing an external file, an exception in the executing code, etc. No newline at end of file
@@ -61,9 +61,11 b' class Style(object):'
61 ``bg`` as the background color and ``attrs`` as the attributes.
61 ``bg`` as the background color and ``attrs`` as the attributes.
62
62
63 Examples:
63 Examples:
64 >>> Style(COLOR_RED, COLOR_BLACK)
65 <Style fg=red bg=black attrs=0>
64
66
65 >>> Style(COLOR_RED, COLOR_BLACK)
67 >>> Style(COLOR_YELLOW, COLOR_BLUE, A_BOLD|A_UNDERLINE)
66 >>> Style(COLOR_YELLOW, COLOR_BLUE, A_BOLD|A_UNDERLINE)
68 <Style fg=yellow bg=blue attrs=bold|underline>
67 """
69 """
68 self.fg = fg
70 self.fg = fg
69 self.bg = bg
71 self.bg = bg
@@ -83,6 +83,8 b' three extensions points (all of them optional):'
83 maxunicode |0xffff
83 maxunicode |0xffff
84 """
84 """
85
85
86 skip_doctest = True # ignore top-level docstring as a doctest.
87
86 import sys, os, os.path, stat, glob, new, csv, datetime, types
88 import sys, os, os.path, stat, glob, new, csv, datetime, types
87 import itertools, mimetypes, StringIO
89 import itertools, mimetypes, StringIO
88
90
@@ -123,8 +125,7 b' except ImportError:'
123 grp = None
125 grp = None
124
126
125 from IPython.external import simplegeneric
127 from IPython.external import simplegeneric
126
128 from IPython.external import path
127 import path
128
129
129 try:
130 try:
130 from IPython import genutils, generics
131 from IPython import genutils, generics
@@ -1210,8 +1211,12 b' class ils(Table):'
1210 Examples::
1211 Examples::
1211
1212
1212 >>> ils
1213 >>> ils
1214 <class 'IPython.Extensions.ipipe.ils'>
1213 >>> ils("/usr/local/lib/python2.4")
1215 >>> ils("/usr/local/lib/python2.4")
1216 IPython.Extensions.ipipe.ils('/usr/local/lib/python2.4')
1214 >>> ils("~")
1217 >>> ils("~")
1218 IPython.Extensions.ipipe.ils('/home/fperez')
1219 # all-random
1215 """
1220 """
1216 def __init__(self, base=os.curdir, dirs=True, files=True):
1221 def __init__(self, base=os.curdir, dirs=True, files=True):
1217 self.base = os.path.expanduser(base)
1222 self.base = os.path.expanduser(base)
@@ -1248,6 +1253,7 b' class iglob(Table):'
1248 Examples::
1253 Examples::
1249
1254
1250 >>> iglob("*.py")
1255 >>> iglob("*.py")
1256 IPython.Extensions.ipipe.iglob('*.py')
1251 """
1257 """
1252 def __init__(self, glob):
1258 def __init__(self, glob):
1253 self.glob = glob
1259 self.glob = glob
@@ -1273,8 +1279,12 b' class iwalk(Table):'
1273 List all files and directories in a directory and it's subdirectory::
1279 List all files and directories in a directory and it's subdirectory::
1274
1280
1275 >>> iwalk
1281 >>> iwalk
1276 >>> iwalk("/usr/local/lib/python2.4")
1282 <class 'IPython.Extensions.ipipe.iwalk'>
1283 >>> iwalk("/usr/lib")
1284 IPython.Extensions.ipipe.iwalk('/usr/lib')
1277 >>> iwalk("~")
1285 >>> iwalk("~")
1286 IPython.Extensions.ipipe.iwalk('/home/fperez') # random
1287
1278 """
1288 """
1279 def __init__(self, base=os.curdir, dirs=True, files=True):
1289 def __init__(self, base=os.curdir, dirs=True, files=True):
1280 self.base = os.path.expanduser(base)
1290 self.base = os.path.expanduser(base)
@@ -1378,6 +1388,8 b' class ipwd(Table):'
1378 Example::
1388 Example::
1379
1389
1380 >>> ipwd | isort("uid")
1390 >>> ipwd | isort("uid")
1391 <IPython.Extensions.ipipe.isort key='uid' reverse=False at 0x849efec>
1392 # random
1381 """
1393 """
1382 def __iter__(self):
1394 def __iter__(self):
1383 for entry in pwd.getpwall():
1395 for entry in pwd.getpwall():
@@ -1562,6 +1574,7 b' class ienv(Table):'
1562 Example::
1574 Example::
1563
1575
1564 >>> ienv
1576 >>> ienv
1577 <class 'IPython.Extensions.ipipe.ienv'>
1565 """
1578 """
1566
1579
1567 def __iter__(self):
1580 def __iter__(self):
@@ -1583,7 +1596,9 b' class ihist(Table):'
1583 Example::
1596 Example::
1584
1597
1585 >>> ihist
1598 >>> ihist
1586 >>> ihist(True) (raw mode)
1599 <class 'IPython.Extensions.ipipe.ihist'>
1600 >>> ihist(True) # raw mode
1601 <IPython.Extensions.ipipe.ihist object at 0x849602c> # random
1587 """
1602 """
1588 def __init__(self, raw=True):
1603 def __init__(self, raw=True):
1589 self.raw = raw
1604 self.raw = raw
@@ -1618,6 +1633,7 b' class ialias(Table):'
1618 Example::
1633 Example::
1619
1634
1620 >>> ialias
1635 >>> ialias
1636 <class 'IPython.Extensions.ipipe.ialias'>
1621 """
1637 """
1622 def __iter__(self):
1638 def __iter__(self):
1623 api = ipapi.get()
1639 api = ipapi.get()
@@ -1680,7 +1696,11 b' class ix(Table):'
1680 Examples::
1696 Examples::
1681
1697
1682 >>> ix("ps x")
1698 >>> ix("ps x")
1699 IPython.Extensions.ipipe.ix('ps x')
1700
1683 >>> ix("find .") | ifile
1701 >>> ix("find .") | ifile
1702 <IPython.Extensions.ipipe.ieval expr=<class 'IPython.Extensions.ipipe.ifile'> at 0x8509d2c>
1703 # random
1684 """
1704 """
1685 def __init__(self, cmd):
1705 def __init__(self, cmd):
1686 self.cmd = cmd
1706 self.cmd = cmd
@@ -1721,6 +1741,7 b' class ifilter(Pipe):'
1721 >>> ils | ifilter("_.isfile() and size>1000")
1741 >>> ils | ifilter("_.isfile() and size>1000")
1722 >>> igrp | ifilter("len(mem)")
1742 >>> igrp | ifilter("len(mem)")
1723 >>> sys.modules | ifilter(lambda _:_.value is not None)
1743 >>> sys.modules | ifilter(lambda _:_.value is not None)
1744 # all-random
1724 """
1745 """
1725
1746
1726 def __init__(self, expr, globals=None, errors="raiseifallfail"):
1747 def __init__(self, expr, globals=None, errors="raiseifallfail"):
@@ -1811,7 +1832,9 b' class ieval(Pipe):'
1811 Examples::
1832 Examples::
1812
1833
1813 >>> ils | ieval("_.abspath()")
1834 >>> ils | ieval("_.abspath()")
1835 # random
1814 >>> sys.path | ieval(ifile)
1836 >>> sys.path | ieval(ifile)
1837 # random
1815 """
1838 """
1816
1839
1817 def __init__(self, expr, globals=None, errors="raiseifallfail"):
1840 def __init__(self, expr, globals=None, errors="raiseifallfail"):
@@ -1884,6 +1907,8 b' class ienum(Pipe):'
1884
1907
1885 >>> xrange(20) | ieval("_,_*_") | ienum | ifilter("index % 2 == 0") | ieval("object")
1908 >>> xrange(20) | ieval("_,_*_") | ienum | ifilter("index % 2 == 0") | ieval("object")
1886 """
1909 """
1910 skip_doctest = True
1911
1887 def __iter__(self):
1912 def __iter__(self):
1888 fields = ("index", "object")
1913 fields = ("index", "object")
1889 for (index, object) in enumerate(xiter(self.input)):
1914 for (index, object) in enumerate(xiter(self.input)):
@@ -1897,7 +1922,10 b' class isort(Pipe):'
1897 Examples::
1922 Examples::
1898
1923
1899 >>> ils | isort("size")
1924 >>> ils | isort("size")
1925 <IPython.Extensions.ipipe.isort key='size' reverse=False at 0x849ec2c>
1900 >>> ils | isort("_.isdir(), _.lower()", reverse=True)
1926 >>> ils | isort("_.isdir(), _.lower()", reverse=True)
1927 <IPython.Extensions.ipipe.isort key='_.isdir(), _.lower()' reverse=True at 0x849eacc>
1928 # all-random
1901 """
1929 """
1902
1930
1903 def __init__(self, key=None, globals=None, reverse=False):
1931 def __init__(self, key=None, globals=None, reverse=False):
@@ -2058,6 +2086,8 b' class icap(Table):'
2058 >>> icap("for i in range(10): print i, time.sleep(0.1)")
2086 >>> icap("for i in range(10): print i, time.sleep(0.1)")
2059
2087
2060 """
2088 """
2089 skip_doctest = True
2090
2061 def __init__(self, expr, globals=None):
2091 def __init__(self, expr, globals=None):
2062 self.expr = expr
2092 self.expr = expr
2063 self.globals = globals
2093 self.globals = globals
@@ -174,23 +174,15 b' class LeoNode(object, UserDict.DictMixin):'
174
174
175 def __get_h(self): return self.p.headString()
175 def __get_h(self): return self.p.headString()
176 def __set_h(self,val):
176 def __set_h(self,val):
177 print "set head",val
177 c.setHeadString(self.p,val)
178 c.beginUpdate()
178 c.redraw()
179 try:
180 c.setHeadString(self.p,val)
181 finally:
182 c.endUpdate()
183
179
184 h = property( __get_h, __set_h, doc = "Node headline string")
180 h = property( __get_h, __set_h, doc = "Node headline string")
185
181
186 def __get_b(self): return self.p.bodyString()
182 def __get_b(self): return self.p.bodyString()
187 def __set_b(self,val):
183 def __set_b(self,val):
188 print "set body",val
184 c.setBodyString(self.p, val)
189 c.beginUpdate()
185 c.redraw()
190 try:
191 c.setBodyString(self.p, val)
192 finally:
193 c.endUpdate()
194
186
195 b = property(__get_b, __set_b, doc = "Nody body string")
187 b = property(__get_b, __set_b, doc = "Nody body string")
196
188
@@ -265,11 +257,8 b' class LeoNode(object, UserDict.DictMixin):'
265
257
266 def go(self):
258 def go(self):
267 """ Set node as current node (to quickly see it in Outline) """
259 """ Set node as current node (to quickly see it in Outline) """
268 c.beginUpdate()
260 c.setCurrentPosition(self.p)
269 try:
261 c.redraw()
270 c.setCurrentPosition(self.p)
271 finally:
272 c.endUpdate()
273
262
274 def script(self):
263 def script(self):
275 """ Method to get the 'tangled' contents of the node
264 """ Method to get the 'tangled' contents of the node
@@ -337,7 +326,6 b' def workbook_complete(obj, prev):'
337
326
338
327
339 def add_var(varname):
328 def add_var(varname):
340 c.beginUpdate()
341 r = rootnode()
329 r = rootnode()
342 try:
330 try:
343 if r is None:
331 if r is None:
@@ -356,7 +344,7 b' def add_var(varname):'
356 c.setHeadString(p2,varname)
344 c.setHeadString(p2,varname)
357 return LeoNode(p2)
345 return LeoNode(p2)
358 finally:
346 finally:
359 c.endUpdate()
347 c.redraw()
360
348
361 def add_file(self,fname):
349 def add_file(self,fname):
362 p2 = c.currentPosition().insertAfter()
350 p2 = c.currentPosition().insertAfter()
@@ -368,7 +356,6 b' def expose_ileo_push(f, prio = 0):'
368
356
369 def push_ipython_script(node):
357 def push_ipython_script(node):
370 """ Execute the node body in IPython, as if it was entered in interactive prompt """
358 """ Execute the node body in IPython, as if it was entered in interactive prompt """
371 c.beginUpdate()
372 try:
359 try:
373 ohist = ip.IP.output_hist
360 ohist = ip.IP.output_hist
374 hstart = len(ip.IP.input_hist)
361 hstart = len(ip.IP.input_hist)
@@ -393,7 +380,7 b' def push_ipython_script(node):'
393 if not has_output:
380 if not has_output:
394 es('ipy run: %s (%d LL)' %( node.h,len(script)))
381 es('ipy run: %s (%d LL)' %( node.h,len(script)))
395 finally:
382 finally:
396 c.endUpdate()
383 c.redraw()
397
384
398
385
399 def eval_body(body):
386 def eval_body(body):
@@ -495,7 +482,6 b' def lee_f(self,s):'
495 """
482 """
496 import os
483 import os
497
484
498 c.beginUpdate()
499 try:
485 try:
500 if s == 'hist':
486 if s == 'hist':
501 wb.ipython_history.b = get_history()
487 wb.ipython_history.b = get_history()
@@ -533,7 +519,7 b' def lee_f(self,s):'
533 c.selectPosition(p)
519 c.selectPosition(p)
534 print "Editing file(s), press ctrl+shift+w in Leo to write @auto nodes"
520 print "Editing file(s), press ctrl+shift+w in Leo to write @auto nodes"
535 finally:
521 finally:
536 c.endUpdate()
522 c.redraw()
537
523
538
524
539
525
@@ -61,6 +61,8 b' from IPython import platutils'
61 import IPython.generics
61 import IPython.generics
62 import IPython.ipapi
62 import IPython.ipapi
63 from IPython.ipapi import UsageError
63 from IPython.ipapi import UsageError
64 from IPython.testing import decorators as testdec
65
64 #***************************************************************************
66 #***************************************************************************
65 # Utility functions
67 # Utility functions
66 def on_off(tag):
68 def on_off(tag):
@@ -522,7 +524,7 b' Currently the magic system has the following functions:\\n"""'
522 rc.automagic = not rc.automagic
524 rc.automagic = not rc.automagic
523 print '\n' + Magic.auto_status[rc.automagic]
525 print '\n' + Magic.auto_status[rc.automagic]
524
526
525
527 @testdec.skip_doctest
526 def magic_autocall(self, parameter_s = ''):
528 def magic_autocall(self, parameter_s = ''):
527 """Make functions callable without having to type parentheses.
529 """Make functions callable without having to type parentheses.
528
530
@@ -551,8 +553,9 b' Currently the magic system has the following functions:\\n"""'
551 2 -> Active always. Even if no arguments are present, the callable
553 2 -> Active always. Even if no arguments are present, the callable
552 object is called:
554 object is called:
553
555
554 In [4]: callable
556 In [2]: float
555 ------> callable()
557 ------> float()
558 Out[2]: 0.0
556
559
557 Note that even with autocall off, you can still use '/' at the start of
560 Note that even with autocall off, you can still use '/' at the start of
558 a line to treat the first argument on the command line as a function
561 a line to treat the first argument on the command line as a function
@@ -561,6 +564,8 b' Currently the magic system has the following functions:\\n"""'
561 In [8]: /str 43
564 In [8]: /str 43
562 ------> str(43)
565 ------> str(43)
563 Out[8]: '43'
566 Out[8]: '43'
567
568 # all-random (note for auto-testing)
564 """
569 """
565
570
566 rc = self.shell.rc
571 rc = self.shell.rc
@@ -1243,12 +1248,13 b' Currently the magic system has the following functions:\\n"""'
1243
1248
1244 self.shell.debugger(force=True)
1249 self.shell.debugger(force=True)
1245
1250
1251 @testdec.skip_doctest
1246 def magic_prun(self, parameter_s ='',user_mode=1,
1252 def magic_prun(self, parameter_s ='',user_mode=1,
1247 opts=None,arg_lst=None,prog_ns=None):
1253 opts=None,arg_lst=None,prog_ns=None):
1248
1254
1249 """Run a statement through the python code profiler.
1255 """Run a statement through the python code profiler.
1250
1256
1251 Usage:\\
1257 Usage:
1252 %prun [options] statement
1258 %prun [options] statement
1253
1259
1254 The given statement (which doesn't require quote marks) is run via the
1260 The given statement (which doesn't require quote marks) is run via the
@@ -1293,16 +1299,16 b' Currently the magic system has the following functions:\\n"""'
1293 abbreviation is unambiguous. The following are the keys currently
1299 abbreviation is unambiguous. The following are the keys currently
1294 defined:
1300 defined:
1295
1301
1296 Valid Arg Meaning\\
1302 Valid Arg Meaning
1297 "calls" call count\\
1303 "calls" call count
1298 "cumulative" cumulative time\\
1304 "cumulative" cumulative time
1299 "file" file name\\
1305 "file" file name
1300 "module" file name\\
1306 "module" file name
1301 "pcalls" primitive call count\\
1307 "pcalls" primitive call count
1302 "line" line number\\
1308 "line" line number
1303 "name" function name\\
1309 "name" function name
1304 "nfl" name/file/line\\
1310 "nfl" name/file/line
1305 "stdname" standard name\\
1311 "stdname" standard name
1306 "time" internal time
1312 "time" internal time
1307
1313
1308 Note that all sorts on statistics are in descending order (placing
1314 Note that all sorts on statistics are in descending order (placing
@@ -1328,8 +1334,10 b' Currently the magic system has the following functions:\\n"""'
1328 '%run -p [prof_opts] filename.py [args to program]' where prof_opts
1334 '%run -p [prof_opts] filename.py [args to program]' where prof_opts
1329 contains profiler specific options as described here.
1335 contains profiler specific options as described here.
1330
1336
1331 You can read the complete documentation for the profile module with:\\
1337 You can read the complete documentation for the profile module with::
1332 In [1]: import profile; profile.help() """
1338
1339 In [1]: import profile; profile.help()
1340 """
1333
1341
1334 opts_def = Struct(D=[''],l=[],s=['time'],T=[''])
1342 opts_def = Struct(D=[''],l=[],s=['time'],T=[''])
1335 # protect user quote marks
1343 # protect user quote marks
@@ -1413,6 +1421,7 b' Currently the magic system has the following functions:\\n"""'
1413 else:
1421 else:
1414 return None
1422 return None
1415
1423
1424 @testdec.skip_doctest
1416 def magic_run(self, parameter_s ='',runner=None):
1425 def magic_run(self, parameter_s ='',runner=None):
1417 """Run the named file inside IPython as a program.
1426 """Run the named file inside IPython as a program.
1418
1427
@@ -1575,12 +1584,16 b' Currently the magic system has the following functions:\\n"""'
1575
1584
1576 # pickle fix. See iplib for an explanation. But we need to make sure
1585 # pickle fix. See iplib for an explanation. But we need to make sure
1577 # that, if we overwrite __main__, we replace it at the end
1586 # that, if we overwrite __main__, we replace it at the end
1578 if prog_ns['__name__'] == '__main__':
1587 main_mod_name = prog_ns['__name__']
1588
1589 if main_mod_name == '__main__':
1579 restore_main = sys.modules['__main__']
1590 restore_main = sys.modules['__main__']
1580 else:
1591 else:
1581 restore_main = False
1592 restore_main = False
1582
1593
1583 sys.modules[prog_ns['__name__']] = main_mod
1594 # This needs to be undone at the end to prevent holding references to
1595 # every single object ever created.
1596 sys.modules[main_mod_name] = main_mod
1584
1597
1585 stats = None
1598 stats = None
1586 try:
1599 try:
@@ -1673,9 +1686,15 b' Currently the magic system has the following functions:\\n"""'
1673 del prog_ns['__name__']
1686 del prog_ns['__name__']
1674 self.shell.user_ns.update(prog_ns)
1687 self.shell.user_ns.update(prog_ns)
1675 finally:
1688 finally:
1689 # Ensure key global structures are restored
1676 sys.argv = save_argv
1690 sys.argv = save_argv
1677 if restore_main:
1691 if restore_main:
1678 sys.modules['__main__'] = restore_main
1692 sys.modules['__main__'] = restore_main
1693 else:
1694 # Remove from sys.modules the reference to main_mod we'd
1695 # added. Otherwise it will trap references to objects
1696 # contained therein.
1697 del sys.modules[main_mod_name]
1679 self.shell.reloadhist()
1698 self.shell.reloadhist()
1680
1699
1681 return stats
1700 return stats
@@ -1699,6 +1718,7 b' Currently the magic system has the following functions:\\n"""'
1699 self.shell.safe_execfile(f,self.shell.user_ns,
1718 self.shell.safe_execfile(f,self.shell.user_ns,
1700 self.shell.user_ns,islog=1)
1719 self.shell.user_ns,islog=1)
1701
1720
1721 @testdec.skip_doctest
1702 def magic_timeit(self, parameter_s =''):
1722 def magic_timeit(self, parameter_s =''):
1703 """Time execution of a Python statement or expression
1723 """Time execution of a Python statement or expression
1704
1724
@@ -1726,7 +1746,8 b' Currently the magic system has the following functions:\\n"""'
1726 Default: 3
1746 Default: 3
1727
1747
1728
1748
1729 Examples:\\
1749 Examples:
1750
1730 In [1]: %timeit pass
1751 In [1]: %timeit pass
1731 10000000 loops, best of 3: 53.3 ns per loop
1752 10000000 loops, best of 3: 53.3 ns per loop
1732
1753
@@ -1755,7 +1776,7 b' Currently the magic system has the following functions:\\n"""'
1755 import timeit
1776 import timeit
1756 import math
1777 import math
1757
1778
1758 units = ["s", "ms", "\xc2\xb5s", "ns"]
1779 units = [u"s", u"ms", u"\xb5s", u"ns"]
1759 scaling = [1, 1e3, 1e6, 1e9]
1780 scaling = [1, 1e3, 1e6, 1e9]
1760
1781
1761 opts, stmt = self.parse_options(parameter_s,'n:r:tcp:',
1782 opts, stmt = self.parse_options(parameter_s,'n:r:tcp:',
@@ -1804,13 +1825,14 b' Currently the magic system has the following functions:\\n"""'
1804 order = min(-int(math.floor(math.log10(best)) // 3), 3)
1825 order = min(-int(math.floor(math.log10(best)) // 3), 3)
1805 else:
1826 else:
1806 order = 3
1827 order = 3
1807 print "%d loops, best of %d: %.*g %s per loop" % (number, repeat,
1828 print u"%d loops, best of %d: %.*g %s per loop" % (number, repeat,
1808 precision,
1829 precision,
1809 best * scaling[order],
1830 best * scaling[order],
1810 units[order])
1831 units[order])
1811 if tc > tc_min:
1832 if tc > tc_min:
1812 print "Compiler time: %.2f s" % tc
1833 print "Compiler time: %.2f s" % tc
1813
1834
1835 @testdec.skip_doctest
1814 def magic_time(self,parameter_s = ''):
1836 def magic_time(self,parameter_s = ''):
1815 """Time execution of a Python statement or expression.
1837 """Time execution of a Python statement or expression.
1816
1838
@@ -1902,6 +1924,7 b' Currently the magic system has the following functions:\\n"""'
1902 print "Compiler : %.2f s" % tc
1924 print "Compiler : %.2f s" % tc
1903 return out
1925 return out
1904
1926
1927 @testdec.skip_doctest
1905 def magic_macro(self,parameter_s = ''):
1928 def magic_macro(self,parameter_s = ''):
1906 """Define a set of input lines as a macro for future re-execution.
1929 """Define a set of input lines as a macro for future re-execution.
1907
1930
@@ -1931,17 +1954,17 b' Currently the magic system has the following functions:\\n"""'
1931
1954
1932 For example, if your history contains (%hist prints it):
1955 For example, if your history contains (%hist prints it):
1933
1956
1934 44: x=1\\
1957 44: x=1
1935 45: y=3\\
1958 45: y=3
1936 46: z=x+y\\
1959 46: z=x+y
1937 47: print x\\
1960 47: print x
1938 48: a=5\\
1961 48: a=5
1939 49: print 'x',x,'y',y\\
1962 49: print 'x',x,'y',y
1940
1963
1941 you can create a macro with lines 44 through 47 (included) and line 49
1964 you can create a macro with lines 44 through 47 (included) and line 49
1942 called my_macro with:
1965 called my_macro with:
1943
1966
1944 In [51]: %macro my_macro 44-47 49
1967 In [55]: %macro my_macro 44-47 49
1945
1968
1946 Now, typing `my_macro` (without quotes) will re-execute all this code
1969 Now, typing `my_macro` (without quotes) will re-execute all this code
1947 in one pass.
1970 in one pass.
@@ -2033,6 +2056,7 b' Currently the magic system has the following functions:\\n"""'
2033 """Alias to %edit."""
2056 """Alias to %edit."""
2034 return self.magic_edit(parameter_s)
2057 return self.magic_edit(parameter_s)
2035
2058
2059 @testdec.skip_doctest
2036 def magic_edit(self,parameter_s='',last_call=['','']):
2060 def magic_edit(self,parameter_s='',last_call=['','']):
2037 """Bring up an editor and execute the resulting code.
2061 """Bring up an editor and execute the resulting code.
2038
2062
@@ -2126,47 +2150,47 b' Currently the magic system has the following functions:\\n"""'
2126 This is an example of creating a simple function inside the editor and
2150 This is an example of creating a simple function inside the editor and
2127 then modifying it. First, start up the editor:
2151 then modifying it. First, start up the editor:
2128
2152
2129 In [1]: ed\\
2153 In [1]: ed
2130 Editing... done. Executing edited code...\\
2154 Editing... done. Executing edited code...
2131 Out[1]: 'def foo():\\n print "foo() was defined in an editing session"\\n'
2155 Out[1]: 'def foo():n print "foo() was defined in an editing session"n'
2132
2156
2133 We can then call the function foo():
2157 We can then call the function foo():
2134
2158
2135 In [2]: foo()\\
2159 In [2]: foo()
2136 foo() was defined in an editing session
2160 foo() was defined in an editing session
2137
2161
2138 Now we edit foo. IPython automatically loads the editor with the
2162 Now we edit foo. IPython automatically loads the editor with the
2139 (temporary) file where foo() was previously defined:
2163 (temporary) file where foo() was previously defined:
2140
2164
2141 In [3]: ed foo\\
2165 In [3]: ed foo
2142 Editing... done. Executing edited code...
2166 Editing... done. Executing edited code...
2143
2167
2144 And if we call foo() again we get the modified version:
2168 And if we call foo() again we get the modified version:
2145
2169
2146 In [4]: foo()\\
2170 In [4]: foo()
2147 foo() has now been changed!
2171 foo() has now been changed!
2148
2172
2149 Here is an example of how to edit a code snippet successive
2173 Here is an example of how to edit a code snippet successive
2150 times. First we call the editor:
2174 times. First we call the editor:
2151
2175
2152 In [8]: ed\\
2176 In [5]: ed
2153 Editing... done. Executing edited code...\\
2177 Editing... done. Executing edited code...
2154 hello\\
2178 hello
2155 Out[8]: "print 'hello'\\n"
2179 Out[5]: "print 'hello'n"
2156
2180
2157 Now we call it again with the previous output (stored in _):
2181 Now we call it again with the previous output (stored in _):
2158
2182
2159 In [9]: ed _\\
2183 In [6]: ed _
2160 Editing... done. Executing edited code...\\
2184 Editing... done. Executing edited code...
2161 hello world\\
2185 hello world
2162 Out[9]: "print 'hello world'\\n"
2186 Out[6]: "print 'hello world'n"
2163
2187
2164 Now we call it with the output #8 (stored in _8, also as Out[8]):
2188 Now we call it with the output #8 (stored in _8, also as Out[8]):
2165
2189
2166 In [10]: ed _8\\
2190 In [7]: ed _8
2167 Editing... done. Executing edited code...\\
2191 Editing... done. Executing edited code...
2168 hello again\\
2192 hello again
2169 Out[10]: "print 'hello again'\\n"
2193 Out[7]: "print 'hello again'n"
2170
2194
2171
2195
2172 Changing the default editor hook:
2196 Changing the default editor hook:
@@ -2463,7 +2487,8 b' Defaulting color scheme to \'NoColor\'"""'
2463
2487
2464 #......................................................................
2488 #......................................................................
2465 # Functions to implement unix shell-type things
2489 # Functions to implement unix shell-type things
2466
2490
2491 @testdec.skip_doctest
2467 def magic_alias(self, parameter_s = ''):
2492 def magic_alias(self, parameter_s = ''):
2468 """Define an alias for a system command.
2493 """Define an alias for a system command.
2469
2494
@@ -2479,18 +2504,18 b' Defaulting color scheme to \'NoColor\'"""'
2479 You can use the %l specifier in an alias definition to represent the
2504 You can use the %l specifier in an alias definition to represent the
2480 whole line when the alias is called. For example:
2505 whole line when the alias is called. For example:
2481
2506
2482 In [2]: alias all echo "Input in brackets: <%l>"\\
2507 In [2]: alias all echo "Input in brackets: <%l>"
2483 In [3]: all hello world\\
2508 In [3]: all hello world
2484 Input in brackets: <hello world>
2509 Input in brackets: <hello world>
2485
2510
2486 You can also define aliases with parameters using %s specifiers (one
2511 You can also define aliases with parameters using %s specifiers (one
2487 per parameter):
2512 per parameter):
2488
2513
2489 In [1]: alias parts echo first %s second %s\\
2514 In [1]: alias parts echo first %s second %s
2490 In [2]: %parts A B\\
2515 In [2]: %parts A B
2491 first A second B\\
2516 first A second B
2492 In [3]: %parts A\\
2517 In [3]: %parts A
2493 Incorrect number of arguments: 2 expected.\\
2518 Incorrect number of arguments: 2 expected.
2494 parts is an alias to: 'echo first %s second %s'
2519 parts is an alias to: 'echo first %s second %s'
2495
2520
2496 Note that %l and %s are mutually exclusive. You can only use one or
2521 Note that %l and %s are mutually exclusive. You can only use one or
@@ -2503,11 +2528,11 b' Defaulting color scheme to \'NoColor\'"""'
2503 IPython for variable expansion. If you want to access a true shell
2528 IPython for variable expansion. If you want to access a true shell
2504 variable, an extra $ is necessary to prevent its expansion by IPython:
2529 variable, an extra $ is necessary to prevent its expansion by IPython:
2505
2530
2506 In [6]: alias show echo\\
2531 In [6]: alias show echo
2507 In [7]: PATH='A Python string'\\
2532 In [7]: PATH='A Python string'
2508 In [8]: show $PATH\\
2533 In [8]: show $PATH
2509 A Python string\\
2534 A Python string
2510 In [9]: show $$PATH\\
2535 In [9]: show $$PATH
2511 /usr/local/lf9560/bin:/usr/local/intel/compiler70/ia32/bin:...
2536 /usr/local/lf9560/bin:/usr/local/intel/compiler70/ia32/bin:...
2512
2537
2513 You can use the alias facility to acess all of $PATH. See the %rehash
2538 You can use the alias facility to acess all of $PATH. See the %rehash
@@ -2822,7 +2847,7 b' Defaulting color scheme to \'NoColor\'"""'
2822 header = 'Directory history (kept in _dh)',
2847 header = 'Directory history (kept in _dh)',
2823 start=ini,stop=fin)
2848 start=ini,stop=fin)
2824
2849
2825
2850 @testdec.skip_doctest
2826 def magic_sc(self, parameter_s=''):
2851 def magic_sc(self, parameter_s=''):
2827 """Shell capture - execute a shell command and capture its output.
2852 """Shell capture - execute a shell command and capture its output.
2828
2853
@@ -2866,31 +2891,33 b' Defaulting color scheme to \'NoColor\'"""'
2866
2891
2867 For example:
2892 For example:
2868
2893
2894 # all-random
2895
2869 # Capture into variable a
2896 # Capture into variable a
2870 In [9]: sc a=ls *py
2897 In [1]: sc a=ls *py
2871
2898
2872 # a is a string with embedded newlines
2899 # a is a string with embedded newlines
2873 In [10]: a
2900 In [2]: a
2874 Out[10]: 'setup.py\nwin32_manual_post_install.py'
2901 Out[2]: 'setup.py\\nwin32_manual_post_install.py'
2875
2902
2876 # which can be seen as a list:
2903 # which can be seen as a list:
2877 In [11]: a.l
2904 In [3]: a.l
2878 Out[11]: ['setup.py', 'win32_manual_post_install.py']
2905 Out[3]: ['setup.py', 'win32_manual_post_install.py']
2879
2906
2880 # or as a whitespace-separated string:
2907 # or as a whitespace-separated string:
2881 In [12]: a.s
2908 In [4]: a.s
2882 Out[12]: 'setup.py win32_manual_post_install.py'
2909 Out[4]: 'setup.py win32_manual_post_install.py'
2883
2910
2884 # a.s is useful to pass as a single command line:
2911 # a.s is useful to pass as a single command line:
2885 In [13]: !wc -l $a.s
2912 In [5]: !wc -l $a.s
2886 146 setup.py
2913 146 setup.py
2887 130 win32_manual_post_install.py
2914 130 win32_manual_post_install.py
2888 276 total
2915 276 total
2889
2916
2890 # while the list form is useful to loop over:
2917 # while the list form is useful to loop over:
2891 In [14]: for f in a.l:
2918 In [6]: for f in a.l:
2892 ....: !wc -l $f
2919 ...: !wc -l $f
2893 ....:
2920 ...:
2894 146 setup.py
2921 146 setup.py
2895 130 win32_manual_post_install.py
2922 130 win32_manual_post_install.py
2896
2923
@@ -2898,13 +2925,13 b' Defaulting color scheme to \'NoColor\'"""'
2898 the sense that you can equally invoke the .s attribute on them to
2925 the sense that you can equally invoke the .s attribute on them to
2899 automatically get a whitespace-separated string from their contents:
2926 automatically get a whitespace-separated string from their contents:
2900
2927
2901 In [1]: sc -l b=ls *py
2928 In [7]: sc -l b=ls *py
2902
2929
2903 In [2]: b
2930 In [8]: b
2904 Out[2]: ['setup.py', 'win32_manual_post_install.py']
2931 Out[8]: ['setup.py', 'win32_manual_post_install.py']
2905
2932
2906 In [3]: b.s
2933 In [9]: b.s
2907 Out[3]: 'setup.py win32_manual_post_install.py'
2934 Out[9]: 'setup.py win32_manual_post_install.py'
2908
2935
2909 In summary, both the lists and strings used for ouptut capture have
2936 In summary, both the lists and strings used for ouptut capture have
2910 the following special attributes:
2937 the following special attributes:
@@ -3273,6 +3300,7 b' Defaulting color scheme to \'NoColor\'"""'
3273 save_dstore('rc_separate_out',rc.separate_out)
3300 save_dstore('rc_separate_out',rc.separate_out)
3274 save_dstore('rc_separate_out2',rc.separate_out2)
3301 save_dstore('rc_separate_out2',rc.separate_out2)
3275 save_dstore('rc_prompts_pad_left',rc.prompts_pad_left)
3302 save_dstore('rc_prompts_pad_left',rc.prompts_pad_left)
3303 save_dstore('rc_separate_in',rc.separate_in)
3276
3304
3277 if mode == False:
3305 if mode == False:
3278 # turn on
3306 # turn on
@@ -3282,6 +3310,8 b' Defaulting color scheme to \'NoColor\'"""'
3282 oc.prompt2.p_template = '... '
3310 oc.prompt2.p_template = '... '
3283 oc.prompt_out.p_template = ''
3311 oc.prompt_out.p_template = ''
3284
3312
3313 # Prompt separators like plain python
3314 oc.input_sep = oc.prompt1.sep = ''
3285 oc.output_sep = ''
3315 oc.output_sep = ''
3286 oc.output_sep2 = ''
3316 oc.output_sep2 = ''
3287
3317
@@ -3300,6 +3330,8 b' Defaulting color scheme to \'NoColor\'"""'
3300 oc.prompt2.p_template = rc.prompt_in2
3330 oc.prompt2.p_template = rc.prompt_in2
3301 oc.prompt_out.p_template = rc.prompt_out
3331 oc.prompt_out.p_template = rc.prompt_out
3302
3332
3333 oc.input_sep = oc.prompt1.sep = dstore.rc_separate_in
3334
3303 oc.output_sep = dstore.rc_separate_out
3335 oc.output_sep = dstore.rc_separate_out
3304 oc.output_sep2 = dstore.rc_separate_out2
3336 oc.output_sep2 = dstore.rc_separate_out2
3305
3337
@@ -24,16 +24,17 b" __all__ = ['Inspector','InspectColors']"
24
24
25 # stdlib modules
25 # stdlib modules
26 import __builtin__
26 import __builtin__
27 import StringIO
27 import inspect
28 import inspect
28 import linecache
29 import linecache
29 import string
30 import StringIO
31 import types
32 import os
30 import os
31 import string
33 import sys
32 import sys
33 import types
34
34 # IPython's own
35 # IPython's own
35 from IPython import PyColorize
36 from IPython import PyColorize
36 from IPython.genutils import page,indent,Term,mkdict
37 from IPython.genutils import page,indent,Term
37 from IPython.Itpl import itpl
38 from IPython.Itpl import itpl
38 from IPython.wildcard import list_namespace
39 from IPython.wildcard import list_namespace
39 from IPython.ColorANSI import *
40 from IPython.ColorANSI import *
@@ -136,6 +137,7 b' def getdoc(obj):'
136 ds = '%s\n%s' % (ds,ds2)
137 ds = '%s\n%s' % (ds,ds2)
137 return ds
138 return ds
138
139
140
139 def getsource(obj,is_binary=False):
141 def getsource(obj,is_binary=False):
140 """Wrapper around inspect.getsource.
142 """Wrapper around inspect.getsource.
141
143
@@ -162,6 +164,26 b' def getsource(obj,is_binary=False):'
162 src = inspect.getsource(obj.__class__)
164 src = inspect.getsource(obj.__class__)
163 return src
165 return src
164
166
167 def getargspec(obj):
168 """Get the names and default values of a function's arguments.
169
170 A tuple of four things is returned: (args, varargs, varkw, defaults).
171 'args' is a list of the argument names (it may contain nested lists).
172 'varargs' and 'varkw' are the names of the * and ** arguments or None.
173 'defaults' is an n-tuple of the default values of the last n arguments.
174
175 Modified version of inspect.getargspec from the Python Standard
176 Library."""
177
178 if inspect.isfunction(obj):
179 func_obj = obj
180 elif inspect.ismethod(obj):
181 func_obj = obj.im_func
182 else:
183 raise TypeError, 'arg is not a Python function'
184 args, varargs, varkw = inspect.getargs(func_obj.func_code)
185 return args, varargs, varkw, func_obj.func_defaults
186
165 #****************************************************************************
187 #****************************************************************************
166 # Class definitions
188 # Class definitions
167
189
@@ -172,6 +194,7 b' class myStringIO(StringIO.StringIO):'
172 self.write(*arg,**kw)
194 self.write(*arg,**kw)
173 self.write('\n')
195 self.write('\n')
174
196
197
175 class Inspector:
198 class Inspector:
176 def __init__(self,color_table,code_color_table,scheme,
199 def __init__(self,color_table,code_color_table,scheme,
177 str_detail_level=0):
200 str_detail_level=0):
@@ -181,26 +204,6 b' class Inspector:'
181 self.str_detail_level = str_detail_level
204 self.str_detail_level = str_detail_level
182 self.set_active_scheme(scheme)
205 self.set_active_scheme(scheme)
183
206
184 def __getargspec(self,obj):
185 """Get the names and default values of a function's arguments.
186
187 A tuple of four things is returned: (args, varargs, varkw, defaults).
188 'args' is a list of the argument names (it may contain nested lists).
189 'varargs' and 'varkw' are the names of the * and ** arguments or None.
190 'defaults' is an n-tuple of the default values of the last n arguments.
191
192 Modified version of inspect.getargspec from the Python Standard
193 Library."""
194
195 if inspect.isfunction(obj):
196 func_obj = obj
197 elif inspect.ismethod(obj):
198 func_obj = obj.im_func
199 else:
200 raise TypeError, 'arg is not a Python function'
201 args, varargs, varkw = inspect.getargs(func_obj.func_code)
202 return args, varargs, varkw, func_obj.func_defaults
203
204 def __getdef(self,obj,oname=''):
207 def __getdef(self,obj,oname=''):
205 """Return the definition header for any callable object.
208 """Return the definition header for any callable object.
206
209
@@ -208,7 +211,7 b' class Inspector:'
208 exception is suppressed."""
211 exception is suppressed."""
209
212
210 try:
213 try:
211 return oname + inspect.formatargspec(*self.__getargspec(obj))
214 return oname + inspect.formatargspec(*getargspec(obj))
212 except:
215 except:
213 return None
216 return None
214
217
@@ -46,13 +46,6 b' from IPython.ipmaker import make_IPython'
46 from IPython.Magic import Magic
46 from IPython.Magic import Magic
47 from IPython.ipstruct import Struct
47 from IPython.ipstruct import Struct
48
48
49 try: # Python 2.3 compatibility
50 set
51 except NameError:
52 import sets
53 set = sets.Set
54
55
56 # Globals
49 # Globals
57 # global flag to pass around information about Ctrl-C without exceptions
50 # global flag to pass around information about Ctrl-C without exceptions
58 KBINT = False
51 KBINT = False
@@ -66,6 +59,9 b' MAIN_THREAD_ID = thread.get_ident()'
66 # Tag when runcode() is active, for exception handling
59 # Tag when runcode() is active, for exception handling
67 CODE_RUN = None
60 CODE_RUN = None
68
61
62 # Default timeout for waiting for multithreaded shells (in seconds)
63 GUI_TIMEOUT = 10
64
69 #-----------------------------------------------------------------------------
65 #-----------------------------------------------------------------------------
70 # This class is trivial now, but I want to have it in to publish a clean
66 # This class is trivial now, but I want to have it in to publish a clean
71 # interface. Later when the internals are reorganized, code that uses this
67 # interface. Later when the internals are reorganized, code that uses this
@@ -359,12 +355,15 b' class MTInteractiveShell(InteractiveShell):'
359 isthreaded = True
355 isthreaded = True
360
356
361 def __init__(self,name,usage=None,rc=Struct(opts=None,args=None),
357 def __init__(self,name,usage=None,rc=Struct(opts=None,args=None),
362 user_ns=None,user_global_ns=None,banner2='',**kw):
358 user_ns=None,user_global_ns=None,banner2='',
359 gui_timeout=GUI_TIMEOUT,**kw):
363 """Similar to the normal InteractiveShell, but with threading control"""
360 """Similar to the normal InteractiveShell, but with threading control"""
364
361
365 InteractiveShell.__init__(self,name,usage,rc,user_ns,
362 InteractiveShell.__init__(self,name,usage,rc,user_ns,
366 user_global_ns,banner2)
363 user_global_ns,banner2)
367
364
365 # Timeout we wait for GUI thread
366 self.gui_timeout = gui_timeout
368
367
369 # A queue to hold the code to be executed.
368 # A queue to hold the code to be executed.
370 self.code_queue = Queue.Queue()
369 self.code_queue = Queue.Queue()
@@ -408,11 +407,12 b' class MTInteractiveShell(InteractiveShell):'
408 # Case 2
407 # Case 2
409 return True
408 return True
410
409
411 # shortcut - if we are in worker thread, or the worker thread is not running,
410 # shortcut - if we are in worker thread, or the worker thread is not
412 # execute directly (to allow recursion and prevent deadlock if code is run early
411 # running, execute directly (to allow recursion and prevent deadlock if
413 # in IPython construction)
412 # code is run early in IPython construction)
414
413
415 if (self.worker_ident is None or self.worker_ident == thread.get_ident()):
414 if (self.worker_ident is None
415 or self.worker_ident == thread.get_ident() ):
416 InteractiveShell.runcode(self,code)
416 InteractiveShell.runcode(self,code)
417 return
417 return
418
418
@@ -423,7 +423,7 b' class MTInteractiveShell(InteractiveShell):'
423
423
424 self.code_queue.put((code,completed_ev, received_ev))
424 self.code_queue.put((code,completed_ev, received_ev))
425 # first make sure the message was received, with timeout
425 # first make sure the message was received, with timeout
426 received_ev.wait(5)
426 received_ev.wait(self.gui_timeout)
427 if not received_ev.isSet():
427 if not received_ev.isSet():
428 # the mainloop is dead, start executing code directly
428 # the mainloop is dead, start executing code directly
429 print "Warning: Timeout for mainloop thread exceeded"
429 print "Warning: Timeout for mainloop thread exceeded"
@@ -39,8 +39,8 b' $Id: __init__.py 2399 2007-05-26 10:23:10Z vivainio $"""'
39 # Enforce proper version requirements
39 # Enforce proper version requirements
40 import sys
40 import sys
41
41
42 if sys.version[0:3] < '2.3':
42 if sys.version[0:3] < '2.4':
43 raise ImportError('Python Version 2.3 or above is required for IPython.')
43 raise ImportError('Python Version 2.4 or above is required for IPython.')
44
44
45 # Make it easy to import extensions - they are always directly on pythonpath.
45 # Make it easy to import extensions - they are always directly on pythonpath.
46 # Therefore, non-IPython modules can be added to Extensions directory
46 # Therefore, non-IPython modules can be added to Extensions directory
@@ -54,6 +54,7 b" __all__ = ['ipapi','generics','ipstruct','Release','Shell']"
54 # access to them via IPython.<name>
54 # access to them via IPython.<name>
55 glob,loc = globals(),locals()
55 glob,loc = globals(),locals()
56 for name in __all__:
56 for name in __all__:
57 #print 'Importing: ',name # dbg
57 __import__(name,glob,loc,[])
58 __import__(name,glob,loc,[])
58
59
59 import Shell
60 import Shell
@@ -108,13 +108,6 b' class Completer:'
108 readline.set_completer(Completer(my_namespace).complete)
108 readline.set_completer(Completer(my_namespace).complete)
109 """
109 """
110
110
111 # some minimal strict typechecks. For some core data structures, I
112 # want actual basic python types, not just anything that looks like
113 # one. This is especially true for namespaces.
114 for ns in (namespace,global_namespace):
115 if ns is not None and type(ns) != types.DictType:
116 raise TypeError,'namespace must be a dictionary'
117
118 # Don't bind to namespace quite yet, but flag whether the user wants a
111 # Don't bind to namespace quite yet, but flag whether the user wants a
119 # specific namespace or to use __main__.__dict__. This will allow us
112 # specific namespace or to use __main__.__dict__. This will allow us
120 # to bind to __main__.__dict__ at completion time, not now.
113 # to bind to __main__.__dict__ at completion time, not now.
@@ -4,6 +4,7 b' A module to change reload() so that it acts recursively.'
4 To enable it type:
4 To enable it type:
5 >>> import __builtin__, deep_reload
5 >>> import __builtin__, deep_reload
6 >>> __builtin__.reload = deep_reload.reload
6 >>> __builtin__.reload = deep_reload.reload
7
7 You can then disable it with:
8 You can then disable it with:
8 >>> __builtin__.reload = deep_reload.original_reload
9 >>> __builtin__.reload = deep_reload.original_reload
9
10
@@ -45,9 +45,9 b' from IPython.frontend.asyncfrontendbase import AsyncFrontEndBase'
45 from twisted.internet.threads import blockingCallFromThread
45 from twisted.internet.threads import blockingCallFromThread
46 from twisted.python.failure import Failure
46 from twisted.python.failure import Failure
47
47
48 #------------------------------------------------------------------------------
48 #-----------------------------------------------------------------------------
49 # Classes to implement the Cocoa frontend
49 # Classes to implement the Cocoa frontend
50 #------------------------------------------------------------------------------
50 #-----------------------------------------------------------------------------
51
51
52 # TODO:
52 # TODO:
53 # 1. use MultiEngineClient and out-of-process engine rather than
53 # 1. use MultiEngineClient and out-of-process engine rather than
@@ -61,41 +61,94 b' class AutoreleasePoolWrappedThreadedEngineService(ThreadedEngineService):'
61 """wrapped_execute"""
61 """wrapped_execute"""
62 try:
62 try:
63 p = NSAutoreleasePool.alloc().init()
63 p = NSAutoreleasePool.alloc().init()
64 result = self.shell.execute(lines)
64 result = super(AutoreleasePoolWrappedThreadedEngineService,
65 except Exception,e:
65 self).wrapped_execute(msg, lines)
66 # This gives the following:
67 # et=exception class
68 # ev=exception class instance
69 # tb=traceback object
70 et,ev,tb = sys.exc_info()
71 # This call adds attributes to the exception value
72 et,ev,tb = self.shell.formatTraceback(et,ev,tb,msg)
73 # Add another attribute
74
75 # Create a new exception with the new attributes
76 e = et(ev._ipython_traceback_text)
77 e._ipython_engine_info = msg
78
79 # Re-raise
80 raise e
81 finally:
66 finally:
82 p.drain()
67 p.drain()
83
68
84 return result
69 return result
85
70
86 def execute(self, lines):
71
87 # Only import this if we are going to use this class
72
88 from twisted.internet import threads
73 class Cell(NSObject):
74 """
75 Representation of the prompts, input and output of a cell in the
76 frontend
77 """
78
79 blockNumber = objc.ivar().unsigned_long()
80 blockID = objc.ivar()
81 inputBlock = objc.ivar()
82 output = objc.ivar()
83
84
85
86 class CellBlock(object):
87 """
88 Storage for information about text ranges relating to a single cell
89 """
90
89
91
90 msg = {'engineid':self.id,
92 def __init__(self, inputPromptRange, inputRange=None, outputPromptRange=None,
91 'method':'execute',
93 outputRange=None):
92 'args':[lines]}
94 super(CellBlock, self).__init__()
95 self.inputPromptRange = inputPromptRange
96 self.inputRange = inputRange
97 self.outputPromptRange = outputPromptRange
98 self.outputRange = outputRange
99
100 def update_ranges_for_insertion(self, text, textRange):
101 """Update ranges for text insertion at textRange"""
102
103 for r in [self.inputPromptRange,self.inputRange,
104 self.outputPromptRange, self.outputRange]:
105 if(r == None):
106 continue
107 intersection = NSIntersectionRange(r,textRange)
108 if(intersection.length == 0): #ranges don't intersect
109 if r.location >= textRange.location:
110 r.location += len(text)
111 else: #ranges intersect
112 if(r.location > textRange.location):
113 offset = len(text) - intersection.length
114 r.length -= offset
115 r.location += offset
116 elif(r.location == textRange.location):
117 r.length += len(text) - intersection.length
118 else:
119 r.length -= intersection.length
120
121
122 def update_ranges_for_deletion(self, textRange):
123 """Update ranges for text deletion at textRange"""
93
124
94 d = threads.deferToThread(self.wrapped_execute, msg, lines)
125 for r in [self.inputPromptRange,self.inputRange,
95 d.addCallback(self.addIDToResult)
126 self.outputPromptRange, self.outputRange]:
96 return d
127 if(r==None):
128 continue
129 intersection = NSIntersectionRange(r, textRange)
130 if(intersection.length == 0): #ranges don't intersect
131 if r.location >= textRange.location:
132 r.location -= textRange.length
133 else: #ranges intersect
134 if(r.location > textRange.location):
135 offset = intersection.length
136 r.length -= offset
137 r.location += offset
138 elif(r.location == textRange.location):
139 r.length += intersection.length
140 else:
141 r.length -= intersection.length
142
143 def __repr__(self):
144 return 'CellBlock('+ str((self.inputPromptRange,
145 self.inputRange,
146 self.outputPromptRange,
147 self.outputRange)) + ')'
148
97
149
98
150
151
99 class IPythonCocoaController(NSObject, AsyncFrontEndBase):
152 class IPythonCocoaController(NSObject, AsyncFrontEndBase):
100 userNS = objc.ivar() #mirror of engine.user_ns (key=>str(value))
153 userNS = objc.ivar() #mirror of engine.user_ns (key=>str(value))
101 waitingForEngine = objc.ivar().bool()
154 waitingForEngine = objc.ivar().bool()
@@ -120,7 +173,7 b' class IPythonCocoaController(NSObject, AsyncFrontEndBase):'
120 self.tabSpaces = 4
173 self.tabSpaces = 4
121 self.tabUsesSpaces = True
174 self.tabUsesSpaces = True
122 self.currentBlockID = self.next_block_ID()
175 self.currentBlockID = self.next_block_ID()
123 self.blockRanges = {} # blockID=>NSRange
176 self.blockRanges = {} # blockID=>CellBlock
124
177
125
178
126 def awakeFromNib(self):
179 def awakeFromNib(self):
@@ -148,6 +201,7 b' class IPythonCocoaController(NSObject, AsyncFrontEndBase):'
148 self.verticalRulerView = r
201 self.verticalRulerView = r
149 self.verticalRulerView.setClientView_(self.textView)
202 self.verticalRulerView.setClientView_(self.textView)
150 self._start_cli_banner()
203 self._start_cli_banner()
204 self.start_new_block()
151
205
152
206
153 def appWillTerminate_(self, notification):
207 def appWillTerminate_(self, notification):
@@ -239,14 +293,16 b' class IPythonCocoaController(NSObject, AsyncFrontEndBase):'
239
293
240
294
241 def update_cell_prompt(self, result, blockID=None):
295 def update_cell_prompt(self, result, blockID=None):
296 print self.blockRanges
242 if(isinstance(result, Failure)):
297 if(isinstance(result, Failure)):
243 self.insert_text(self.input_prompt(),
298 prompt = self.input_prompt()
244 textRange=NSMakeRange(self.blockRanges[blockID].location,0),
299
245 scrollToVisible=False
246 )
247 else:
300 else:
248 self.insert_text(self.input_prompt(number=result['number']),
301 prompt = self.input_prompt(number=result['number'])
249 textRange=NSMakeRange(self.blockRanges[blockID].location,0),
302
303 r = self.blockRanges[blockID].inputPromptRange
304 self.insert_text(prompt,
305 textRange=r,
250 scrollToVisible=False
306 scrollToVisible=False
251 )
307 )
252
308
@@ -255,7 +311,7 b' class IPythonCocoaController(NSObject, AsyncFrontEndBase):'
255
311
256 def render_result(self, result):
312 def render_result(self, result):
257 blockID = result['blockID']
313 blockID = result['blockID']
258 inputRange = self.blockRanges[blockID]
314 inputRange = self.blockRanges[blockID].inputRange
259 del self.blockRanges[blockID]
315 del self.blockRanges[blockID]
260
316
261 #print inputRange,self.current_block_range()
317 #print inputRange,self.current_block_range()
@@ -269,11 +325,17 b' class IPythonCocoaController(NSObject, AsyncFrontEndBase):'
269
325
270
326
271 def render_error(self, failure):
327 def render_error(self, failure):
328 print failure
329 blockID = failure.blockID
330 inputRange = self.blockRanges[blockID].inputRange
272 self.insert_text('\n' +
331 self.insert_text('\n' +
273 self.output_prompt() +
332 self.output_prompt() +
274 '\n' +
333 '\n' +
275 failure.getErrorMessage() +
334 failure.getErrorMessage() +
276 '\n\n')
335 '\n\n',
336 textRange=NSMakeRange(inputRange.location +
337 inputRange.length,
338 0))
277 self.start_new_block()
339 self.start_new_block()
278 return failure
340 return failure
279
341
@@ -291,6 +353,9 b' class IPythonCocoaController(NSObject, AsyncFrontEndBase):'
291 """"""
353 """"""
292
354
293 self.currentBlockID = self.next_block_ID()
355 self.currentBlockID = self.next_block_ID()
356 self.blockRanges[self.currentBlockID] = self.new_cell_block()
357 self.insert_text(self.input_prompt(),
358 textRange=self.current_block_range().inputPromptRange)
294
359
295
360
296
361
@@ -298,15 +363,23 b' class IPythonCocoaController(NSObject, AsyncFrontEndBase):'
298
363
299 return uuid.uuid4()
364 return uuid.uuid4()
300
365
366 def new_cell_block(self):
367 """A new CellBlock at the end of self.textView.textStorage()"""
368
369 return CellBlock(NSMakeRange(self.textView.textStorage().length(),
370 0), #len(self.input_prompt())),
371 NSMakeRange(self.textView.textStorage().length(),# + len(self.input_prompt()),
372 0))
373
374
301 def current_block_range(self):
375 def current_block_range(self):
302 return self.blockRanges.get(self.currentBlockID,
376 return self.blockRanges.get(self.currentBlockID,
303 NSMakeRange(self.textView.textStorage().length(),
377 self.new_cell_block())
304 0))
305
378
306 def current_block(self):
379 def current_block(self):
307 """The current block's text"""
380 """The current block's text"""
308
381
309 return self.text_for_range(self.current_block_range())
382 return self.text_for_range(self.current_block_range().inputRange)
310
383
311 def text_for_range(self, textRange):
384 def text_for_range(self, textRange):
312 """text_for_range"""
385 """text_for_range"""
@@ -315,7 +388,7 b' class IPythonCocoaController(NSObject, AsyncFrontEndBase):'
315 return ts.string().substringWithRange_(textRange)
388 return ts.string().substringWithRange_(textRange)
316
389
317 def current_line(self):
390 def current_line(self):
318 block = self.text_for_range(self.current_block_range())
391 block = self.text_for_range(self.current_block_range().inputRange)
319 block = block.split('\n')
392 block = block.split('\n')
320 return block[-1]
393 return block[-1]
321
394
@@ -324,38 +397,28 b' class IPythonCocoaController(NSObject, AsyncFrontEndBase):'
324 """Insert text into textView at textRange, updating blockRanges
397 """Insert text into textView at textRange, updating blockRanges
325 as necessary
398 as necessary
326 """
399 """
327
328 if(textRange == None):
400 if(textRange == None):
329 #range for end of text
401 #range for end of text
330 textRange = NSMakeRange(self.textView.textStorage().length(), 0)
402 textRange = NSMakeRange(self.textView.textStorage().length(), 0)
331
403
332 for r in self.blockRanges.itervalues():
333 intersection = NSIntersectionRange(r,textRange)
334 if(intersection.length == 0): #ranges don't intersect
335 if r.location >= textRange.location:
336 r.location += len(string)
337 else: #ranges intersect
338 if(r.location <= textRange.location):
339 assert(intersection.length == textRange.length)
340 r.length += textRange.length
341 else:
342 r.location += intersection.length
343
404
344 self.textView.replaceCharactersInRange_withString_(
405 self.textView.replaceCharactersInRange_withString_(
345 textRange, string)
406 textRange, string)
346 self.textView.setSelectedRange_(
407
347 NSMakeRange(textRange.location+len(string), 0))
408 for r in self.blockRanges.itervalues():
409 r.update_ranges_for_insertion(string, textRange)
410
411 self.textView.setSelectedRange_(textRange)
348 if(scrollToVisible):
412 if(scrollToVisible):
349 self.textView.scrollRangeToVisible_(textRange)
413 self.textView.scrollRangeToVisible_(textRange)
350
351
414
352
415
353
416
354 def replace_current_block_with_string(self, textView, string):
417 def replace_current_block_with_string(self, textView, string):
355 textView.replaceCharactersInRange_withString_(
418 textView.replaceCharactersInRange_withString_(
356 self.current_block_range(),
419 self.current_block_range().inputRange,
357 string)
420 string)
358 self.current_block_range().length = len(string)
421 self.current_block_range().inputRange.length = len(string)
359 r = NSMakeRange(textView.textStorage().length(), 0)
422 r = NSMakeRange(textView.textStorage().length(), 0)
360 textView.scrollRangeToVisible_(r)
423 textView.scrollRangeToVisible_(r)
361 textView.setSelectedRange_(r)
424 textView.setSelectedRange_(r)
@@ -424,26 +487,18 b' class IPythonCocoaController(NSObject, AsyncFrontEndBase):'
424
487
425 elif(selector == 'moveToBeginningOfParagraph:'):
488 elif(selector == 'moveToBeginningOfParagraph:'):
426 textView.setSelectedRange_(NSMakeRange(
489 textView.setSelectedRange_(NSMakeRange(
427 self.current_block_range().location,
490 self.current_block_range().inputRange.location,
428 0))
491 0))
429 return True
492 return True
430 elif(selector == 'moveToEndOfParagraph:'):
493 elif(selector == 'moveToEndOfParagraph:'):
431 textView.setSelectedRange_(NSMakeRange(
494 textView.setSelectedRange_(NSMakeRange(
432 self.current_block_range().location + \
495 self.current_block_range().inputRange.location + \
433 self.current_block_range().length, 0))
496 self.current_block_range().inputRange.length, 0))
434 return True
497 return True
435 elif(selector == 'deleteToEndOfParagraph:'):
498 elif(selector == 'deleteToEndOfParagraph:'):
436 if(textView.selectedRange().location <= \
499 if(textView.selectedRange().location <= \
437 self.current_block_range().location):
500 self.current_block_range().location):
438 # Intersect the selected range with the current line range
501 raise NotImplemented()
439 if(self.current_block_range().length < 0):
440 self.blockRanges[self.currentBlockID].length = 0
441
442 r = NSIntersectionRange(textView.rangesForUserTextChange()[0],
443 self.current_block_range())
444
445 if(r.length > 0): #no intersection
446 textView.setSelectedRange_(r)
447
502
448 return False # don't actually handle the delete
503 return False # don't actually handle the delete
449
504
@@ -457,10 +512,15 b' class IPythonCocoaController(NSObject, AsyncFrontEndBase):'
457 elif(selector == 'deleteBackward:'):
512 elif(selector == 'deleteBackward:'):
458 #if we're at the beginning of the current block, ignore
513 #if we're at the beginning of the current block, ignore
459 if(textView.selectedRange().location == \
514 if(textView.selectedRange().location == \
460 self.current_block_range().location):
515 self.current_block_range().inputRange.location):
461 return True
516 return True
462 else:
517 else:
463 self.current_block_range().length-=1
518 for r in self.blockRanges.itervalues():
519 deleteRange = textView.selectedRange
520 if(deleteRange.length == 0):
521 deleteRange.location -= 1
522 deleteRange.length = 1
523 r.update_ranges_for_deletion(deleteRange)
464 return False
524 return False
465 return False
525 return False
466
526
@@ -479,14 +539,9 b' class IPythonCocoaController(NSObject, AsyncFrontEndBase):'
479 for r,s in zip(ranges, replacementStrings):
539 for r,s in zip(ranges, replacementStrings):
480 r = r.rangeValue()
540 r = r.rangeValue()
481 if(textView.textStorage().length() > 0 and
541 if(textView.textStorage().length() > 0 and
482 r.location < self.current_block_range().location):
542 r.location < self.current_block_range().inputRange.location):
483 self.insert_text(s)
543 self.insert_text(s)
484 allow = False
544 allow = False
485
486
487 self.blockRanges.setdefault(self.currentBlockID,
488 self.current_block_range()).length +=\
489 len(s)
490
545
491 return allow
546 return allow
492
547
This diff has been collapsed as it changes many lines, (1033 lines changed) Show them Hide them
@@ -37,12 +37,12 b''
37 <string key="NSKeyEquiv" id="255189770"/>
37 <string key="NSKeyEquiv" id="255189770"/>
38 <int key="NSKeyEquivModMask">1048576</int>
38 <int key="NSKeyEquivModMask">1048576</int>
39 <int key="NSMnemonicLoc">2147483647</int>
39 <int key="NSMnemonicLoc">2147483647</int>
40 <object class="NSCustomResource" key="NSOnImage" id="985281305">
40 <object class="NSCustomResource" key="NSOnImage" id="271266416">
41 <string key="NSClassName" id="60114142">NSImage</string>
41 <string key="NSClassName" id="375865337">NSImage</string>
42 <string key="NSResourceName">NSMenuCheckmark</string>
42 <string key="NSResourceName">NSMenuCheckmark</string>
43 </object>
43 </object>
44 <object class="NSCustomResource" key="NSMixedImage" id="351279908">
44 <object class="NSCustomResource" key="NSMixedImage" id="508123839">
45 <reference key="NSClassName" ref="60114142"/>
45 <reference key="NSClassName" ref="375865337"/>
46 <string key="NSResourceName">NSMenuMixedState</string>
46 <string key="NSResourceName">NSMenuMixedState</string>
47 </object>
47 </object>
48 <string key="NSAction">submenuAction:</string>
48 <string key="NSAction">submenuAction:</string>
@@ -55,8 +55,8 b''
55 <string key="NSTitle">About IPython1Sandbox</string>
55 <string key="NSTitle">About IPython1Sandbox</string>
56 <reference key="NSKeyEquiv" ref="255189770"/>
56 <reference key="NSKeyEquiv" ref="255189770"/>
57 <int key="NSMnemonicLoc">2147483647</int>
57 <int key="NSMnemonicLoc">2147483647</int>
58 <reference key="NSOnImage" ref="985281305"/>
58 <reference key="NSOnImage" ref="271266416"/>
59 <reference key="NSMixedImage" ref="351279908"/>
59 <reference key="NSMixedImage" ref="508123839"/>
60 </object>
60 </object>
61 <object class="NSMenuItem" id="304266470">
61 <object class="NSMenuItem" id="304266470">
62 <reference key="NSMenu" ref="110575045"/>
62 <reference key="NSMenu" ref="110575045"/>
@@ -66,8 +66,8 b''
66 <reference key="NSKeyEquiv" ref="255189770"/>
66 <reference key="NSKeyEquiv" ref="255189770"/>
67 <int key="NSKeyEquivModMask">1048576</int>
67 <int key="NSKeyEquivModMask">1048576</int>
68 <int key="NSMnemonicLoc">2147483647</int>
68 <int key="NSMnemonicLoc">2147483647</int>
69 <reference key="NSOnImage" ref="985281305"/>
69 <reference key="NSOnImage" ref="271266416"/>
70 <reference key="NSMixedImage" ref="351279908"/>
70 <reference key="NSMixedImage" ref="508123839"/>
71 </object>
71 </object>
72 <object class="NSMenuItem" id="609285721">
72 <object class="NSMenuItem" id="609285721">
73 <reference key="NSMenu" ref="110575045"/>
73 <reference key="NSMenu" ref="110575045"/>
@@ -75,8 +75,8 b''
75 <string key="NSKeyEquiv">,</string>
75 <string key="NSKeyEquiv">,</string>
76 <int key="NSKeyEquivModMask">1048576</int>
76 <int key="NSKeyEquivModMask">1048576</int>
77 <int key="NSMnemonicLoc">2147483647</int>
77 <int key="NSMnemonicLoc">2147483647</int>
78 <reference key="NSOnImage" ref="985281305"/>
78 <reference key="NSOnImage" ref="271266416"/>
79 <reference key="NSMixedImage" ref="351279908"/>
79 <reference key="NSMixedImage" ref="508123839"/>
80 </object>
80 </object>
81 <object class="NSMenuItem" id="481834944">
81 <object class="NSMenuItem" id="481834944">
82 <reference key="NSMenu" ref="110575045"/>
82 <reference key="NSMenu" ref="110575045"/>
@@ -86,8 +86,8 b''
86 <reference key="NSKeyEquiv" ref="255189770"/>
86 <reference key="NSKeyEquiv" ref="255189770"/>
87 <int key="NSKeyEquivModMask">1048576</int>
87 <int key="NSKeyEquivModMask">1048576</int>
88 <int key="NSMnemonicLoc">2147483647</int>
88 <int key="NSMnemonicLoc">2147483647</int>
89 <reference key="NSOnImage" ref="985281305"/>
89 <reference key="NSOnImage" ref="271266416"/>
90 <reference key="NSMixedImage" ref="351279908"/>
90 <reference key="NSMixedImage" ref="508123839"/>
91 </object>
91 </object>
92 <object class="NSMenuItem" id="1046388886">
92 <object class="NSMenuItem" id="1046388886">
93 <reference key="NSMenu" ref="110575045"/>
93 <reference key="NSMenu" ref="110575045"/>
@@ -95,8 +95,8 b''
95 <reference key="NSKeyEquiv" ref="255189770"/>
95 <reference key="NSKeyEquiv" ref="255189770"/>
96 <int key="NSKeyEquivModMask">1048576</int>
96 <int key="NSKeyEquivModMask">1048576</int>
97 <int key="NSMnemonicLoc">2147483647</int>
97 <int key="NSMnemonicLoc">2147483647</int>
98 <reference key="NSOnImage" ref="985281305"/>
98 <reference key="NSOnImage" ref="271266416"/>
99 <reference key="NSMixedImage" ref="351279908"/>
99 <reference key="NSMixedImage" ref="508123839"/>
100 <string key="NSAction">submenuAction:</string>
100 <string key="NSAction">submenuAction:</string>
101 <object class="NSMenu" key="NSSubmenu" id="752062318">
101 <object class="NSMenu" key="NSSubmenu" id="752062318">
102 <reference key="NSTitle" ref="642338826"/>
102 <reference key="NSTitle" ref="642338826"/>
@@ -114,8 +114,8 b''
114 <reference key="NSKeyEquiv" ref="255189770"/>
114 <reference key="NSKeyEquiv" ref="255189770"/>
115 <int key="NSKeyEquivModMask">1048576</int>
115 <int key="NSKeyEquivModMask">1048576</int>
116 <int key="NSMnemonicLoc">2147483647</int>
116 <int key="NSMnemonicLoc">2147483647</int>
117 <reference key="NSOnImage" ref="985281305"/>
117 <reference key="NSOnImage" ref="271266416"/>
118 <reference key="NSMixedImage" ref="351279908"/>
118 <reference key="NSMixedImage" ref="508123839"/>
119 </object>
119 </object>
120 <object class="NSMenuItem" id="755159360">
120 <object class="NSMenuItem" id="755159360">
121 <reference key="NSMenu" ref="110575045"/>
121 <reference key="NSMenu" ref="110575045"/>
@@ -123,8 +123,8 b''
123 <string key="NSKeyEquiv" id="940330891">h</string>
123 <string key="NSKeyEquiv" id="940330891">h</string>
124 <int key="NSKeyEquivModMask">1048576</int>
124 <int key="NSKeyEquivModMask">1048576</int>
125 <int key="NSMnemonicLoc">2147483647</int>
125 <int key="NSMnemonicLoc">2147483647</int>
126 <reference key="NSOnImage" ref="985281305"/>
126 <reference key="NSOnImage" ref="271266416"/>
127 <reference key="NSMixedImage" ref="351279908"/>
127 <reference key="NSMixedImage" ref="508123839"/>
128 </object>
128 </object>
129 <object class="NSMenuItem" id="342932134">
129 <object class="NSMenuItem" id="342932134">
130 <reference key="NSMenu" ref="110575045"/>
130 <reference key="NSMenu" ref="110575045"/>
@@ -132,8 +132,8 b''
132 <reference key="NSKeyEquiv" ref="940330891"/>
132 <reference key="NSKeyEquiv" ref="940330891"/>
133 <int key="NSKeyEquivModMask">1572864</int>
133 <int key="NSKeyEquivModMask">1572864</int>
134 <int key="NSMnemonicLoc">2147483647</int>
134 <int key="NSMnemonicLoc">2147483647</int>
135 <reference key="NSOnImage" ref="985281305"/>
135 <reference key="NSOnImage" ref="271266416"/>
136 <reference key="NSMixedImage" ref="351279908"/>
136 <reference key="NSMixedImage" ref="508123839"/>
137 </object>
137 </object>
138 <object class="NSMenuItem" id="908899353">
138 <object class="NSMenuItem" id="908899353">
139 <reference key="NSMenu" ref="110575045"/>
139 <reference key="NSMenu" ref="110575045"/>
@@ -141,8 +141,8 b''
141 <reference key="NSKeyEquiv" ref="255189770"/>
141 <reference key="NSKeyEquiv" ref="255189770"/>
142 <int key="NSKeyEquivModMask">1048576</int>
142 <int key="NSKeyEquivModMask">1048576</int>
143 <int key="NSMnemonicLoc">2147483647</int>
143 <int key="NSMnemonicLoc">2147483647</int>
144 <reference key="NSOnImage" ref="985281305"/>
144 <reference key="NSOnImage" ref="271266416"/>
145 <reference key="NSMixedImage" ref="351279908"/>
145 <reference key="NSMixedImage" ref="508123839"/>
146 </object>
146 </object>
147 <object class="NSMenuItem" id="1056857174">
147 <object class="NSMenuItem" id="1056857174">
148 <reference key="NSMenu" ref="110575045"/>
148 <reference key="NSMenu" ref="110575045"/>
@@ -152,8 +152,8 b''
152 <reference key="NSKeyEquiv" ref="255189770"/>
152 <reference key="NSKeyEquiv" ref="255189770"/>
153 <int key="NSKeyEquivModMask">1048576</int>
153 <int key="NSKeyEquivModMask">1048576</int>
154 <int key="NSMnemonicLoc">2147483647</int>
154 <int key="NSMnemonicLoc">2147483647</int>
155 <reference key="NSOnImage" ref="985281305"/>
155 <reference key="NSOnImage" ref="271266416"/>
156 <reference key="NSMixedImage" ref="351279908"/>
156 <reference key="NSMixedImage" ref="508123839"/>
157 </object>
157 </object>
158 <object class="NSMenuItem" id="632727374">
158 <object class="NSMenuItem" id="632727374">
159 <reference key="NSMenu" ref="110575045"/>
159 <reference key="NSMenu" ref="110575045"/>
@@ -161,8 +161,8 b''
161 <string key="NSKeyEquiv">q</string>
161 <string key="NSKeyEquiv">q</string>
162 <int key="NSKeyEquivModMask">1048576</int>
162 <int key="NSKeyEquivModMask">1048576</int>
163 <int key="NSMnemonicLoc">2147483647</int>
163 <int key="NSMnemonicLoc">2147483647</int>
164 <reference key="NSOnImage" ref="985281305"/>
164 <reference key="NSOnImage" ref="271266416"/>
165 <reference key="NSMixedImage" ref="351279908"/>
165 <reference key="NSMixedImage" ref="508123839"/>
166 </object>
166 </object>
167 </object>
167 </object>
168 <string key="NSName">_NSAppleMenu</string>
168 <string key="NSName">_NSAppleMenu</string>
@@ -174,8 +174,8 b''
174 <reference key="NSKeyEquiv" ref="255189770"/>
174 <reference key="NSKeyEquiv" ref="255189770"/>
175 <int key="NSKeyEquivModMask">1048576</int>
175 <int key="NSKeyEquivModMask">1048576</int>
176 <int key="NSMnemonicLoc">2147483647</int>
176 <int key="NSMnemonicLoc">2147483647</int>
177 <reference key="NSOnImage" ref="985281305"/>
177 <reference key="NSOnImage" ref="271266416"/>
178 <reference key="NSMixedImage" ref="351279908"/>
178 <reference key="NSMixedImage" ref="508123839"/>
179 <string key="NSAction">submenuAction:</string>
179 <string key="NSAction">submenuAction:</string>
180 <object class="NSMenu" key="NSSubmenu" id="720053764">
180 <object class="NSMenu" key="NSSubmenu" id="720053764">
181 <reference key="NSTitle" ref="881404960"/>
181 <reference key="NSTitle" ref="881404960"/>
@@ -187,8 +187,8 b''
187 <string key="NSKeyEquiv">n</string>
187 <string key="NSKeyEquiv">n</string>
188 <int key="NSKeyEquivModMask">1048576</int>
188 <int key="NSKeyEquivModMask">1048576</int>
189 <int key="NSMnemonicLoc">2147483647</int>
189 <int key="NSMnemonicLoc">2147483647</int>
190 <reference key="NSOnImage" ref="985281305"/>
190 <reference key="NSOnImage" ref="271266416"/>
191 <reference key="NSMixedImage" ref="351279908"/>
191 <reference key="NSMixedImage" ref="508123839"/>
192 </object>
192 </object>
193 <object class="NSMenuItem" id="722745758">
193 <object class="NSMenuItem" id="722745758">
194 <reference key="NSMenu" ref="720053764"/>
194 <reference key="NSMenu" ref="720053764"/>
@@ -196,8 +196,8 b''
196 <string key="NSKeyEquiv">o</string>
196 <string key="NSKeyEquiv">o</string>
197 <int key="NSKeyEquivModMask">1048576</int>
197 <int key="NSKeyEquivModMask">1048576</int>
198 <int key="NSMnemonicLoc">2147483647</int>
198 <int key="NSMnemonicLoc">2147483647</int>
199 <reference key="NSOnImage" ref="985281305"/>
199 <reference key="NSOnImage" ref="271266416"/>
200 <reference key="NSMixedImage" ref="351279908"/>
200 <reference key="NSMixedImage" ref="508123839"/>
201 </object>
201 </object>
202 <object class="NSMenuItem" id="1025936716">
202 <object class="NSMenuItem" id="1025936716">
203 <reference key="NSMenu" ref="720053764"/>
203 <reference key="NSMenu" ref="720053764"/>
@@ -205,8 +205,8 b''
205 <reference key="NSKeyEquiv" ref="255189770"/>
205 <reference key="NSKeyEquiv" ref="255189770"/>
206 <int key="NSKeyEquivModMask">1048576</int>
206 <int key="NSKeyEquivModMask">1048576</int>
207 <int key="NSMnemonicLoc">2147483647</int>
207 <int key="NSMnemonicLoc">2147483647</int>
208 <reference key="NSOnImage" ref="985281305"/>
208 <reference key="NSOnImage" ref="271266416"/>
209 <reference key="NSMixedImage" ref="351279908"/>
209 <reference key="NSMixedImage" ref="508123839"/>
210 <string key="NSAction">submenuAction:</string>
210 <string key="NSAction">submenuAction:</string>
211 <object class="NSMenu" key="NSSubmenu" id="1065607017">
211 <object class="NSMenu" key="NSSubmenu" id="1065607017">
212 <reference key="NSTitle" ref="975517829"/>
212 <reference key="NSTitle" ref="975517829"/>
@@ -218,8 +218,8 b''
218 <reference key="NSKeyEquiv" ref="255189770"/>
218 <reference key="NSKeyEquiv" ref="255189770"/>
219 <int key="NSKeyEquivModMask">1048576</int>
219 <int key="NSKeyEquivModMask">1048576</int>
220 <int key="NSMnemonicLoc">2147483647</int>
220 <int key="NSMnemonicLoc">2147483647</int>
221 <reference key="NSOnImage" ref="985281305"/>
221 <reference key="NSOnImage" ref="271266416"/>
222 <reference key="NSMixedImage" ref="351279908"/>
222 <reference key="NSMixedImage" ref="508123839"/>
223 </object>
223 </object>
224 </object>
224 </object>
225 <string key="NSName">_NSRecentDocumentsMenu</string>
225 <string key="NSName">_NSRecentDocumentsMenu</string>
@@ -233,8 +233,8 b''
233 <reference key="NSKeyEquiv" ref="255189770"/>
233 <reference key="NSKeyEquiv" ref="255189770"/>
234 <int key="NSKeyEquivModMask">1048576</int>
234 <int key="NSKeyEquivModMask">1048576</int>
235 <int key="NSMnemonicLoc">2147483647</int>
235 <int key="NSMnemonicLoc">2147483647</int>
236 <reference key="NSOnImage" ref="985281305"/>
236 <reference key="NSOnImage" ref="271266416"/>
237 <reference key="NSMixedImage" ref="351279908"/>
237 <reference key="NSMixedImage" ref="508123839"/>
238 </object>
238 </object>
239 <object class="NSMenuItem" id="776162233">
239 <object class="NSMenuItem" id="776162233">
240 <reference key="NSMenu" ref="720053764"/>
240 <reference key="NSMenu" ref="720053764"/>
@@ -242,8 +242,8 b''
242 <string key="NSKeyEquiv">w</string>
242 <string key="NSKeyEquiv">w</string>
243 <int key="NSKeyEquivModMask">1048576</int>
243 <int key="NSKeyEquivModMask">1048576</int>
244 <int key="NSMnemonicLoc">2147483647</int>
244 <int key="NSMnemonicLoc">2147483647</int>
245 <reference key="NSOnImage" ref="985281305"/>
245 <reference key="NSOnImage" ref="271266416"/>
246 <reference key="NSMixedImage" ref="351279908"/>
246 <reference key="NSMixedImage" ref="508123839"/>
247 </object>
247 </object>
248 <object class="NSMenuItem" id="1023925487">
248 <object class="NSMenuItem" id="1023925487">
249 <reference key="NSMenu" ref="720053764"/>
249 <reference key="NSMenu" ref="720053764"/>
@@ -251,8 +251,8 b''
251 <string key="NSKeyEquiv">s</string>
251 <string key="NSKeyEquiv">s</string>
252 <int key="NSKeyEquivModMask">1048576</int>
252 <int key="NSKeyEquivModMask">1048576</int>
253 <int key="NSMnemonicLoc">2147483647</int>
253 <int key="NSMnemonicLoc">2147483647</int>
254 <reference key="NSOnImage" ref="985281305"/>
254 <reference key="NSOnImage" ref="271266416"/>
255 <reference key="NSMixedImage" ref="351279908"/>
255 <reference key="NSMixedImage" ref="508123839"/>
256 </object>
256 </object>
257 <object class="NSMenuItem" id="117038363">
257 <object class="NSMenuItem" id="117038363">
258 <reference key="NSMenu" ref="720053764"/>
258 <reference key="NSMenu" ref="720053764"/>
@@ -260,16 +260,16 b''
260 <string key="NSKeyEquiv">S</string>
260 <string key="NSKeyEquiv">S</string>
261 <int key="NSKeyEquivModMask">1179648</int>
261 <int key="NSKeyEquivModMask">1179648</int>
262 <int key="NSMnemonicLoc">2147483647</int>
262 <int key="NSMnemonicLoc">2147483647</int>
263 <reference key="NSOnImage" ref="985281305"/>
263 <reference key="NSOnImage" ref="271266416"/>
264 <reference key="NSMixedImage" ref="351279908"/>
264 <reference key="NSMixedImage" ref="508123839"/>
265 </object>
265 </object>
266 <object class="NSMenuItem" id="579971712">
266 <object class="NSMenuItem" id="579971712">
267 <reference key="NSMenu" ref="720053764"/>
267 <reference key="NSMenu" ref="720053764"/>
268 <string key="NSTitle">Revert to Saved</string>
268 <string key="NSTitle">Revert to Saved</string>
269 <reference key="NSKeyEquiv" ref="255189770"/>
269 <reference key="NSKeyEquiv" ref="255189770"/>
270 <int key="NSMnemonicLoc">2147483647</int>
270 <int key="NSMnemonicLoc">2147483647</int>
271 <reference key="NSOnImage" ref="985281305"/>
271 <reference key="NSOnImage" ref="271266416"/>
272 <reference key="NSMixedImage" ref="351279908"/>
272 <reference key="NSMixedImage" ref="508123839"/>
273 </object>
273 </object>
274 <object class="NSMenuItem" id="1010469920">
274 <object class="NSMenuItem" id="1010469920">
275 <reference key="NSMenu" ref="720053764"/>
275 <reference key="NSMenu" ref="720053764"/>
@@ -279,8 +279,8 b''
279 <reference key="NSKeyEquiv" ref="255189770"/>
279 <reference key="NSKeyEquiv" ref="255189770"/>
280 <int key="NSKeyEquivModMask">1048576</int>
280 <int key="NSKeyEquivModMask">1048576</int>
281 <int key="NSMnemonicLoc">2147483647</int>
281 <int key="NSMnemonicLoc">2147483647</int>
282 <reference key="NSOnImage" ref="985281305"/>
282 <reference key="NSOnImage" ref="271266416"/>
283 <reference key="NSMixedImage" ref="351279908"/>
283 <reference key="NSMixedImage" ref="508123839"/>
284 </object>
284 </object>
285 <object class="NSMenuItem" id="294629803">
285 <object class="NSMenuItem" id="294629803">
286 <reference key="NSMenu" ref="720053764"/>
286 <reference key="NSMenu" ref="720053764"/>
@@ -288,8 +288,8 b''
288 <string key="NSKeyEquiv">P</string>
288 <string key="NSKeyEquiv">P</string>
289 <int key="NSKeyEquivModMask">1179648</int>
289 <int key="NSKeyEquivModMask">1179648</int>
290 <int key="NSMnemonicLoc">2147483647</int>
290 <int key="NSMnemonicLoc">2147483647</int>
291 <reference key="NSOnImage" ref="985281305"/>
291 <reference key="NSOnImage" ref="271266416"/>
292 <reference key="NSMixedImage" ref="351279908"/>
292 <reference key="NSMixedImage" ref="508123839"/>
293 <reference key="NSToolTip" ref="255189770"/>
293 <reference key="NSToolTip" ref="255189770"/>
294 </object>
294 </object>
295 <object class="NSMenuItem" id="49223823">
295 <object class="NSMenuItem" id="49223823">
@@ -298,8 +298,8 b''
298 <string key="NSKeyEquiv">p</string>
298 <string key="NSKeyEquiv">p</string>
299 <int key="NSKeyEquivModMask">1048576</int>
299 <int key="NSKeyEquivModMask">1048576</int>
300 <int key="NSMnemonicLoc">2147483647</int>
300 <int key="NSMnemonicLoc">2147483647</int>
301 <reference key="NSOnImage" ref="985281305"/>
301 <reference key="NSOnImage" ref="271266416"/>
302 <reference key="NSMixedImage" ref="351279908"/>
302 <reference key="NSMixedImage" ref="508123839"/>
303 </object>
303 </object>
304 </object>
304 </object>
305 </object>
305 </object>
@@ -310,8 +310,8 b''
310 <reference key="NSKeyEquiv" ref="255189770"/>
310 <reference key="NSKeyEquiv" ref="255189770"/>
311 <int key="NSKeyEquivModMask">1048576</int>
311 <int key="NSKeyEquivModMask">1048576</int>
312 <int key="NSMnemonicLoc">2147483647</int>
312 <int key="NSMnemonicLoc">2147483647</int>
313 <reference key="NSOnImage" ref="985281305"/>
313 <reference key="NSOnImage" ref="271266416"/>
314 <reference key="NSMixedImage" ref="351279908"/>
314 <reference key="NSMixedImage" ref="508123839"/>
315 <string key="NSAction">submenuAction:</string>
315 <string key="NSAction">submenuAction:</string>
316 <object class="NSMenu" key="NSSubmenu" id="789758025">
316 <object class="NSMenu" key="NSSubmenu" id="789758025">
317 <reference key="NSTitle" ref="1037326483"/>
317 <reference key="NSTitle" ref="1037326483"/>
@@ -323,8 +323,8 b''
323 <string key="NSKeyEquiv">z</string>
323 <string key="NSKeyEquiv">z</string>
324 <int key="NSKeyEquivModMask">1048576</int>
324 <int key="NSKeyEquivModMask">1048576</int>
325 <int key="NSMnemonicLoc">2147483647</int>
325 <int key="NSMnemonicLoc">2147483647</int>
326 <reference key="NSOnImage" ref="985281305"/>
326 <reference key="NSOnImage" ref="271266416"/>
327 <reference key="NSMixedImage" ref="351279908"/>
327 <reference key="NSMixedImage" ref="508123839"/>
328 </object>
328 </object>
329 <object class="NSMenuItem" id="790794224">
329 <object class="NSMenuItem" id="790794224">
330 <reference key="NSMenu" ref="789758025"/>
330 <reference key="NSMenu" ref="789758025"/>
@@ -332,8 +332,8 b''
332 <string key="NSKeyEquiv">Z</string>
332 <string key="NSKeyEquiv">Z</string>
333 <int key="NSKeyEquivModMask">1179648</int>
333 <int key="NSKeyEquivModMask">1179648</int>
334 <int key="NSMnemonicLoc">2147483647</int>
334 <int key="NSMnemonicLoc">2147483647</int>
335 <reference key="NSOnImage" ref="985281305"/>
335 <reference key="NSOnImage" ref="271266416"/>
336 <reference key="NSMixedImage" ref="351279908"/>
336 <reference key="NSMixedImage" ref="508123839"/>
337 </object>
337 </object>
338 <object class="NSMenuItem" id="1040322652">
338 <object class="NSMenuItem" id="1040322652">
339 <reference key="NSMenu" ref="789758025"/>
339 <reference key="NSMenu" ref="789758025"/>
@@ -343,8 +343,8 b''
343 <reference key="NSKeyEquiv" ref="255189770"/>
343 <reference key="NSKeyEquiv" ref="255189770"/>
344 <int key="NSKeyEquivModMask">1048576</int>
344 <int key="NSKeyEquivModMask">1048576</int>
345 <int key="NSMnemonicLoc">2147483647</int>
345 <int key="NSMnemonicLoc">2147483647</int>
346 <reference key="NSOnImage" ref="985281305"/>
346 <reference key="NSOnImage" ref="271266416"/>
347 <reference key="NSMixedImage" ref="351279908"/>
347 <reference key="NSMixedImage" ref="508123839"/>
348 </object>
348 </object>
349 <object class="NSMenuItem" id="296257095">
349 <object class="NSMenuItem" id="296257095">
350 <reference key="NSMenu" ref="789758025"/>
350 <reference key="NSMenu" ref="789758025"/>
@@ -352,8 +352,8 b''
352 <string key="NSKeyEquiv">x</string>
352 <string key="NSKeyEquiv">x</string>
353 <int key="NSKeyEquivModMask">1048576</int>
353 <int key="NSKeyEquivModMask">1048576</int>
354 <int key="NSMnemonicLoc">2147483647</int>
354 <int key="NSMnemonicLoc">2147483647</int>
355 <reference key="NSOnImage" ref="985281305"/>
355 <reference key="NSOnImage" ref="271266416"/>
356 <reference key="NSMixedImage" ref="351279908"/>
356 <reference key="NSMixedImage" ref="508123839"/>
357 </object>
357 </object>
358 <object class="NSMenuItem" id="860595796">
358 <object class="NSMenuItem" id="860595796">
359 <reference key="NSMenu" ref="789758025"/>
359 <reference key="NSMenu" ref="789758025"/>
@@ -361,8 +361,8 b''
361 <string key="NSKeyEquiv">c</string>
361 <string key="NSKeyEquiv">c</string>
362 <int key="NSKeyEquivModMask">1048576</int>
362 <int key="NSKeyEquivModMask">1048576</int>
363 <int key="NSMnemonicLoc">2147483647</int>
363 <int key="NSMnemonicLoc">2147483647</int>
364 <reference key="NSOnImage" ref="985281305"/>
364 <reference key="NSOnImage" ref="271266416"/>
365 <reference key="NSMixedImage" ref="351279908"/>
365 <reference key="NSMixedImage" ref="508123839"/>
366 </object>
366 </object>
367 <object class="NSMenuItem" id="29853731">
367 <object class="NSMenuItem" id="29853731">
368 <reference key="NSMenu" ref="789758025"/>
368 <reference key="NSMenu" ref="789758025"/>
@@ -370,8 +370,8 b''
370 <string key="NSKeyEquiv">v</string>
370 <string key="NSKeyEquiv">v</string>
371 <int key="NSKeyEquivModMask">1048576</int>
371 <int key="NSKeyEquivModMask">1048576</int>
372 <int key="NSMnemonicLoc">2147483647</int>
372 <int key="NSMnemonicLoc">2147483647</int>
373 <reference key="NSOnImage" ref="985281305"/>
373 <reference key="NSOnImage" ref="271266416"/>
374 <reference key="NSMixedImage" ref="351279908"/>
374 <reference key="NSMixedImage" ref="508123839"/>
375 </object>
375 </object>
376 <object class="NSMenuItem" id="437104165">
376 <object class="NSMenuItem" id="437104165">
377 <reference key="NSMenu" ref="789758025"/>
377 <reference key="NSMenu" ref="789758025"/>
@@ -379,8 +379,8 b''
379 <reference key="NSKeyEquiv" ref="255189770"/>
379 <reference key="NSKeyEquiv" ref="255189770"/>
380 <int key="NSKeyEquivModMask">1048576</int>
380 <int key="NSKeyEquivModMask">1048576</int>
381 <int key="NSMnemonicLoc">2147483647</int>
381 <int key="NSMnemonicLoc">2147483647</int>
382 <reference key="NSOnImage" ref="985281305"/>
382 <reference key="NSOnImage" ref="271266416"/>
383 <reference key="NSMixedImage" ref="351279908"/>
383 <reference key="NSMixedImage" ref="508123839"/>
384 </object>
384 </object>
385 <object class="NSMenuItem" id="583158037">
385 <object class="NSMenuItem" id="583158037">
386 <reference key="NSMenu" ref="789758025"/>
386 <reference key="NSMenu" ref="789758025"/>
@@ -388,8 +388,8 b''
388 <string key="NSKeyEquiv">a</string>
388 <string key="NSKeyEquiv">a</string>
389 <int key="NSKeyEquivModMask">1048576</int>
389 <int key="NSKeyEquivModMask">1048576</int>
390 <int key="NSMnemonicLoc">2147483647</int>
390 <int key="NSMnemonicLoc">2147483647</int>
391 <reference key="NSOnImage" ref="985281305"/>
391 <reference key="NSOnImage" ref="271266416"/>
392 <reference key="NSMixedImage" ref="351279908"/>
392 <reference key="NSMixedImage" ref="508123839"/>
393 </object>
393 </object>
394 <object class="NSMenuItem" id="212016141">
394 <object class="NSMenuItem" id="212016141">
395 <reference key="NSMenu" ref="789758025"/>
395 <reference key="NSMenu" ref="789758025"/>
@@ -399,8 +399,8 b''
399 <reference key="NSKeyEquiv" ref="255189770"/>
399 <reference key="NSKeyEquiv" ref="255189770"/>
400 <int key="NSKeyEquivModMask">1048576</int>
400 <int key="NSKeyEquivModMask">1048576</int>
401 <int key="NSMnemonicLoc">2147483647</int>
401 <int key="NSMnemonicLoc">2147483647</int>
402 <reference key="NSOnImage" ref="985281305"/>
402 <reference key="NSOnImage" ref="271266416"/>
403 <reference key="NSMixedImage" ref="351279908"/>
403 <reference key="NSMixedImage" ref="508123839"/>
404 </object>
404 </object>
405 <object class="NSMenuItem" id="892235320">
405 <object class="NSMenuItem" id="892235320">
406 <reference key="NSMenu" ref="789758025"/>
406 <reference key="NSMenu" ref="789758025"/>
@@ -408,8 +408,8 b''
408 <reference key="NSKeyEquiv" ref="255189770"/>
408 <reference key="NSKeyEquiv" ref="255189770"/>
409 <int key="NSKeyEquivModMask">1048576</int>
409 <int key="NSKeyEquivModMask">1048576</int>
410 <int key="NSMnemonicLoc">2147483647</int>
410 <int key="NSMnemonicLoc">2147483647</int>
411 <reference key="NSOnImage" ref="985281305"/>
411 <reference key="NSOnImage" ref="271266416"/>
412 <reference key="NSMixedImage" ref="351279908"/>
412 <reference key="NSMixedImage" ref="508123839"/>
413 <string key="NSAction">submenuAction:</string>
413 <string key="NSAction">submenuAction:</string>
414 <object class="NSMenu" key="NSSubmenu" id="963351320">
414 <object class="NSMenu" key="NSSubmenu" id="963351320">
415 <reference key="NSTitle" ref="688083180"/>
415 <reference key="NSTitle" ref="688083180"/>
@@ -421,8 +421,8 b''
421 <string key="NSKeyEquiv" id="469505129">f</string>
421 <string key="NSKeyEquiv" id="469505129">f</string>
422 <int key="NSKeyEquivModMask">1048576</int>
422 <int key="NSKeyEquivModMask">1048576</int>
423 <int key="NSMnemonicLoc">2147483647</int>
423 <int key="NSMnemonicLoc">2147483647</int>
424 <reference key="NSOnImage" ref="985281305"/>
424 <reference key="NSOnImage" ref="271266416"/>
425 <reference key="NSMixedImage" ref="351279908"/>
425 <reference key="NSMixedImage" ref="508123839"/>
426 <int key="NSTag">1</int>
426 <int key="NSTag">1</int>
427 </object>
427 </object>
428 <object class="NSMenuItem" id="326711663">
428 <object class="NSMenuItem" id="326711663">
@@ -431,8 +431,8 b''
431 <string key="NSKeyEquiv" id="762398675">g</string>
431 <string key="NSKeyEquiv" id="762398675">g</string>
432 <int key="NSKeyEquivModMask">1048576</int>
432 <int key="NSKeyEquivModMask">1048576</int>
433 <int key="NSMnemonicLoc">2147483647</int>
433 <int key="NSMnemonicLoc">2147483647</int>
434 <reference key="NSOnImage" ref="985281305"/>
434 <reference key="NSOnImage" ref="271266416"/>
435 <reference key="NSMixedImage" ref="351279908"/>
435 <reference key="NSMixedImage" ref="508123839"/>
436 <int key="NSTag">2</int>
436 <int key="NSTag">2</int>
437 </object>
437 </object>
438 <object class="NSMenuItem" id="270902937">
438 <object class="NSMenuItem" id="270902937">
@@ -441,8 +441,8 b''
441 <string key="NSKeyEquiv" id="819654342">G</string>
441 <string key="NSKeyEquiv" id="819654342">G</string>
442 <int key="NSKeyEquivModMask">1179648</int>
442 <int key="NSKeyEquivModMask">1179648</int>
443 <int key="NSMnemonicLoc">2147483647</int>
443 <int key="NSMnemonicLoc">2147483647</int>
444 <reference key="NSOnImage" ref="985281305"/>
444 <reference key="NSOnImage" ref="271266416"/>
445 <reference key="NSMixedImage" ref="351279908"/>
445 <reference key="NSMixedImage" ref="508123839"/>
446 <int key="NSTag">3</int>
446 <int key="NSTag">3</int>
447 </object>
447 </object>
448 <object class="NSMenuItem" id="159080638">
448 <object class="NSMenuItem" id="159080638">
@@ -451,8 +451,8 b''
451 <string key="NSKeyEquiv">e</string>
451 <string key="NSKeyEquiv">e</string>
452 <int key="NSKeyEquivModMask">1048576</int>
452 <int key="NSKeyEquivModMask">1048576</int>
453 <int key="NSMnemonicLoc">2147483647</int>
453 <int key="NSMnemonicLoc">2147483647</int>
454 <reference key="NSOnImage" ref="985281305"/>
454 <reference key="NSOnImage" ref="271266416"/>
455 <reference key="NSMixedImage" ref="351279908"/>
455 <reference key="NSMixedImage" ref="508123839"/>
456 <int key="NSTag">7</int>
456 <int key="NSTag">7</int>
457 </object>
457 </object>
458 <object class="NSMenuItem" id="88285865">
458 <object class="NSMenuItem" id="88285865">
@@ -461,8 +461,8 b''
461 <string key="NSKeyEquiv">j</string>
461 <string key="NSKeyEquiv">j</string>
462 <int key="NSKeyEquivModMask">1048576</int>
462 <int key="NSKeyEquivModMask">1048576</int>
463 <int key="NSMnemonicLoc">2147483647</int>
463 <int key="NSMnemonicLoc">2147483647</int>
464 <reference key="NSOnImage" ref="985281305"/>
464 <reference key="NSOnImage" ref="271266416"/>
465 <reference key="NSMixedImage" ref="351279908"/>
465 <reference key="NSMixedImage" ref="508123839"/>
466 </object>
466 </object>
467 </object>
467 </object>
468 </object>
468 </object>
@@ -473,8 +473,8 b''
473 <reference key="NSKeyEquiv" ref="255189770"/>
473 <reference key="NSKeyEquiv" ref="255189770"/>
474 <int key="NSKeyEquivModMask">1048576</int>
474 <int key="NSKeyEquivModMask">1048576</int>
475 <int key="NSMnemonicLoc">2147483647</int>
475 <int key="NSMnemonicLoc">2147483647</int>
476 <reference key="NSOnImage" ref="985281305"/>
476 <reference key="NSOnImage" ref="271266416"/>
477 <reference key="NSMixedImage" ref="351279908"/>
477 <reference key="NSMixedImage" ref="508123839"/>
478 <string key="NSAction">submenuAction:</string>
478 <string key="NSAction">submenuAction:</string>
479 <object class="NSMenu" key="NSSubmenu" id="769623530">
479 <object class="NSMenu" key="NSSubmenu" id="769623530">
480 <reference key="NSTitle" ref="739167250"/>
480 <reference key="NSTitle" ref="739167250"/>
@@ -486,8 +486,8 b''
486 <string key="NSKeyEquiv">:</string>
486 <string key="NSKeyEquiv">:</string>
487 <int key="NSKeyEquivModMask">1048576</int>
487 <int key="NSKeyEquivModMask">1048576</int>
488 <int key="NSMnemonicLoc">2147483647</int>
488 <int key="NSMnemonicLoc">2147483647</int>
489 <reference key="NSOnImage" ref="985281305"/>
489 <reference key="NSOnImage" ref="271266416"/>
490 <reference key="NSMixedImage" ref="351279908"/>
490 <reference key="NSMixedImage" ref="508123839"/>
491 </object>
491 </object>
492 <object class="NSMenuItem" id="96193923">
492 <object class="NSMenuItem" id="96193923">
493 <reference key="NSMenu" ref="769623530"/>
493 <reference key="NSMenu" ref="769623530"/>
@@ -495,8 +495,8 b''
495 <string key="NSKeyEquiv">;</string>
495 <string key="NSKeyEquiv">;</string>
496 <int key="NSKeyEquivModMask">1048576</int>
496 <int key="NSKeyEquivModMask">1048576</int>
497 <int key="NSMnemonicLoc">2147483647</int>
497 <int key="NSMnemonicLoc">2147483647</int>
498 <reference key="NSOnImage" ref="985281305"/>
498 <reference key="NSOnImage" ref="271266416"/>
499 <reference key="NSMixedImage" ref="351279908"/>
499 <reference key="NSMixedImage" ref="508123839"/>
500 </object>
500 </object>
501 <object class="NSMenuItem" id="948374510">
501 <object class="NSMenuItem" id="948374510">
502 <reference key="NSMenu" ref="769623530"/>
502 <reference key="NSMenu" ref="769623530"/>
@@ -504,8 +504,8 b''
504 <reference key="NSKeyEquiv" ref="255189770"/>
504 <reference key="NSKeyEquiv" ref="255189770"/>
505 <int key="NSKeyEquivModMask">1048576</int>
505 <int key="NSKeyEquivModMask">1048576</int>
506 <int key="NSMnemonicLoc">2147483647</int>
506 <int key="NSMnemonicLoc">2147483647</int>
507 <reference key="NSOnImage" ref="985281305"/>
507 <reference key="NSOnImage" ref="271266416"/>
508 <reference key="NSMixedImage" ref="351279908"/>
508 <reference key="NSMixedImage" ref="508123839"/>
509 </object>
509 </object>
510 <object class="NSMenuItem" id="967646866">
510 <object class="NSMenuItem" id="967646866">
511 <reference key="NSMenu" ref="769623530"/>
511 <reference key="NSMenu" ref="769623530"/>
@@ -513,8 +513,8 b''
513 <reference key="NSKeyEquiv" ref="255189770"/>
513 <reference key="NSKeyEquiv" ref="255189770"/>
514 <int key="NSKeyEquivModMask">1048576</int>
514 <int key="NSKeyEquivModMask">1048576</int>
515 <int key="NSMnemonicLoc">2147483647</int>
515 <int key="NSMnemonicLoc">2147483647</int>
516 <reference key="NSOnImage" ref="985281305"/>
516 <reference key="NSOnImage" ref="271266416"/>
517 <reference key="NSMixedImage" ref="351279908"/>
517 <reference key="NSMixedImage" ref="508123839"/>
518 </object>
518 </object>
519 </object>
519 </object>
520 </object>
520 </object>
@@ -525,8 +525,8 b''
525 <reference key="NSKeyEquiv" ref="255189770"/>
525 <reference key="NSKeyEquiv" ref="255189770"/>
526 <int key="NSKeyEquivModMask">1048576</int>
526 <int key="NSKeyEquivModMask">1048576</int>
527 <int key="NSMnemonicLoc">2147483647</int>
527 <int key="NSMnemonicLoc">2147483647</int>
528 <reference key="NSOnImage" ref="985281305"/>
528 <reference key="NSOnImage" ref="271266416"/>
529 <reference key="NSMixedImage" ref="351279908"/>
529 <reference key="NSMixedImage" ref="508123839"/>
530 <string key="NSAction">submenuAction:</string>
530 <string key="NSAction">submenuAction:</string>
531 <object class="NSMenu" key="NSSubmenu" id="698887838">
531 <object class="NSMenu" key="NSSubmenu" id="698887838">
532 <reference key="NSTitle" ref="904739598"/>
532 <reference key="NSTitle" ref="904739598"/>
@@ -538,8 +538,8 b''
538 <reference key="NSKeyEquiv" ref="469505129"/>
538 <reference key="NSKeyEquiv" ref="469505129"/>
539 <int key="NSKeyEquivModMask">1048576</int>
539 <int key="NSKeyEquivModMask">1048576</int>
540 <int key="NSMnemonicLoc">2147483647</int>
540 <int key="NSMnemonicLoc">2147483647</int>
541 <reference key="NSOnImage" ref="985281305"/>
541 <reference key="NSOnImage" ref="271266416"/>
542 <reference key="NSMixedImage" ref="351279908"/>
542 <reference key="NSMixedImage" ref="508123839"/>
543 <int key="NSTag">1</int>
543 <int key="NSTag">1</int>
544 </object>
544 </object>
545 <object class="NSMenuItem" id="197661976">
545 <object class="NSMenuItem" id="197661976">
@@ -548,8 +548,8 b''
548 <reference key="NSKeyEquiv" ref="762398675"/>
548 <reference key="NSKeyEquiv" ref="762398675"/>
549 <int key="NSKeyEquivModMask">1048576</int>
549 <int key="NSKeyEquivModMask">1048576</int>
550 <int key="NSMnemonicLoc">2147483647</int>
550 <int key="NSMnemonicLoc">2147483647</int>
551 <reference key="NSOnImage" ref="985281305"/>
551 <reference key="NSOnImage" ref="271266416"/>
552 <reference key="NSMixedImage" ref="351279908"/>
552 <reference key="NSMixedImage" ref="508123839"/>
553 <int key="NSTag">2</int>
553 <int key="NSTag">2</int>
554 </object>
554 </object>
555 <object class="NSMenuItem" id="708854459">
555 <object class="NSMenuItem" id="708854459">
@@ -558,8 +558,8 b''
558 <reference key="NSKeyEquiv" ref="819654342"/>
558 <reference key="NSKeyEquiv" ref="819654342"/>
559 <int key="NSKeyEquivModMask">1179648</int>
559 <int key="NSKeyEquivModMask">1179648</int>
560 <int key="NSMnemonicLoc">2147483647</int>
560 <int key="NSMnemonicLoc">2147483647</int>
561 <reference key="NSOnImage" ref="985281305"/>
561 <reference key="NSOnImage" ref="271266416"/>
562 <reference key="NSMixedImage" ref="351279908"/>
562 <reference key="NSMixedImage" ref="508123839"/>
563 <int key="NSTag">3</int>
563 <int key="NSTag">3</int>
564 </object>
564 </object>
565 </object>
565 </object>
@@ -571,8 +571,8 b''
571 <reference key="NSKeyEquiv" ref="255189770"/>
571 <reference key="NSKeyEquiv" ref="255189770"/>
572 <int key="NSKeyEquivModMask">1048576</int>
572 <int key="NSKeyEquivModMask">1048576</int>
573 <int key="NSMnemonicLoc">2147483647</int>
573 <int key="NSMnemonicLoc">2147483647</int>
574 <reference key="NSOnImage" ref="985281305"/>
574 <reference key="NSOnImage" ref="271266416"/>
575 <reference key="NSMixedImage" ref="351279908"/>
575 <reference key="NSMixedImage" ref="508123839"/>
576 <string key="NSAction">submenuAction:</string>
576 <string key="NSAction">submenuAction:</string>
577 <object class="NSMenu" key="NSSubmenu" id="785027613">
577 <object class="NSMenu" key="NSSubmenu" id="785027613">
578 <reference key="NSTitle" ref="812002426"/>
578 <reference key="NSTitle" ref="812002426"/>
@@ -584,8 +584,8 b''
584 <reference key="NSKeyEquiv" ref="255189770"/>
584 <reference key="NSKeyEquiv" ref="255189770"/>
585 <int key="NSKeyEquivModMask">1048576</int>
585 <int key="NSKeyEquivModMask">1048576</int>
586 <int key="NSMnemonicLoc">2147483647</int>
586 <int key="NSMnemonicLoc">2147483647</int>
587 <reference key="NSOnImage" ref="985281305"/>
587 <reference key="NSOnImage" ref="271266416"/>
588 <reference key="NSMixedImage" ref="351279908"/>
588 <reference key="NSMixedImage" ref="508123839"/>
589 </object>
589 </object>
590 <object class="NSMenuItem" id="680220178">
590 <object class="NSMenuItem" id="680220178">
591 <reference key="NSMenu" ref="785027613"/>
591 <reference key="NSMenu" ref="785027613"/>
@@ -593,8 +593,8 b''
593 <reference key="NSKeyEquiv" ref="255189770"/>
593 <reference key="NSKeyEquiv" ref="255189770"/>
594 <int key="NSKeyEquivModMask">1048576</int>
594 <int key="NSKeyEquivModMask">1048576</int>
595 <int key="NSMnemonicLoc">2147483647</int>
595 <int key="NSMnemonicLoc">2147483647</int>
596 <reference key="NSOnImage" ref="985281305"/>
596 <reference key="NSOnImage" ref="271266416"/>
597 <reference key="NSMixedImage" ref="351279908"/>
597 <reference key="NSMixedImage" ref="508123839"/>
598 </object>
598 </object>
599 </object>
599 </object>
600 </object>
600 </object>
@@ -608,8 +608,8 b''
608 <reference key="NSKeyEquiv" ref="255189770"/>
608 <reference key="NSKeyEquiv" ref="255189770"/>
609 <int key="NSKeyEquivModMask">1048576</int>
609 <int key="NSKeyEquivModMask">1048576</int>
610 <int key="NSMnemonicLoc">2147483647</int>
610 <int key="NSMnemonicLoc">2147483647</int>
611 <reference key="NSOnImage" ref="985281305"/>
611 <reference key="NSOnImage" ref="271266416"/>
612 <reference key="NSMixedImage" ref="351279908"/>
612 <reference key="NSMixedImage" ref="508123839"/>
613 <string key="NSAction">submenuAction:</string>
613 <string key="NSAction">submenuAction:</string>
614 <object class="NSMenu" key="NSSubmenu" id="502084290">
614 <object class="NSMenu" key="NSSubmenu" id="502084290">
615 <reference key="NSTitle" ref="241242548"/>
615 <reference key="NSTitle" ref="241242548"/>
@@ -621,8 +621,8 b''
621 <string key="NSKeyEquiv" id="806579634">t</string>
621 <string key="NSKeyEquiv" id="806579634">t</string>
622 <int key="NSKeyEquivModMask">1048576</int>
622 <int key="NSKeyEquivModMask">1048576</int>
623 <int key="NSMnemonicLoc">2147483647</int>
623 <int key="NSMnemonicLoc">2147483647</int>
624 <reference key="NSOnImage" ref="985281305"/>
624 <reference key="NSOnImage" ref="271266416"/>
625 <reference key="NSMixedImage" ref="351279908"/>
625 <reference key="NSMixedImage" ref="508123839"/>
626 </object>
626 </object>
627 <object class="NSMenuItem" id="1028416764">
627 <object class="NSMenuItem" id="1028416764">
628 <reference key="NSMenu" ref="502084290"/>
628 <reference key="NSMenu" ref="502084290"/>
@@ -630,8 +630,8 b''
630 <string key="NSKeyEquiv">C</string>
630 <string key="NSKeyEquiv">C</string>
631 <int key="NSKeyEquivModMask">1179648</int>
631 <int key="NSKeyEquivModMask">1179648</int>
632 <int key="NSMnemonicLoc">2147483647</int>
632 <int key="NSMnemonicLoc">2147483647</int>
633 <reference key="NSOnImage" ref="985281305"/>
633 <reference key="NSOnImage" ref="271266416"/>
634 <reference key="NSMixedImage" ref="351279908"/>
634 <reference key="NSMixedImage" ref="508123839"/>
635 </object>
635 </object>
636 </object>
636 </object>
637 </object>
637 </object>
@@ -642,8 +642,8 b''
642 <reference key="NSKeyEquiv" ref="255189770"/>
642 <reference key="NSKeyEquiv" ref="255189770"/>
643 <int key="NSKeyEquivModMask">1048576</int>
643 <int key="NSKeyEquivModMask">1048576</int>
644 <int key="NSMnemonicLoc">2147483647</int>
644 <int key="NSMnemonicLoc">2147483647</int>
645 <reference key="NSOnImage" ref="985281305"/>
645 <reference key="NSOnImage" ref="271266416"/>
646 <reference key="NSMixedImage" ref="351279908"/>
646 <reference key="NSMixedImage" ref="508123839"/>
647 <string key="NSAction">submenuAction:</string>
647 <string key="NSAction">submenuAction:</string>
648 <object class="NSMenu" key="NSSubmenu" id="466310130">
648 <object class="NSMenu" key="NSSubmenu" id="466310130">
649 <reference key="NSTitle" ref="809723865"/>
649 <reference key="NSTitle" ref="809723865"/>
@@ -655,8 +655,8 b''
655 <reference key="NSKeyEquiv" ref="806579634"/>
655 <reference key="NSKeyEquiv" ref="806579634"/>
656 <int key="NSKeyEquivModMask">1572864</int>
656 <int key="NSKeyEquivModMask">1572864</int>
657 <int key="NSMnemonicLoc">2147483647</int>
657 <int key="NSMnemonicLoc">2147483647</int>
658 <reference key="NSOnImage" ref="985281305"/>
658 <reference key="NSOnImage" ref="271266416"/>
659 <reference key="NSMixedImage" ref="351279908"/>
659 <reference key="NSMixedImage" ref="508123839"/>
660 </object>
660 </object>
661 <object class="NSMenuItem" id="237841660">
661 <object class="NSMenuItem" id="237841660">
662 <reference key="NSMenu" ref="466310130"/>
662 <reference key="NSMenu" ref="466310130"/>
@@ -664,8 +664,8 b''
664 <reference key="NSKeyEquiv" ref="255189770"/>
664 <reference key="NSKeyEquiv" ref="255189770"/>
665 <int key="NSKeyEquivModMask">1048576</int>
665 <int key="NSKeyEquivModMask">1048576</int>
666 <int key="NSMnemonicLoc">2147483647</int>
666 <int key="NSMnemonicLoc">2147483647</int>
667 <reference key="NSOnImage" ref="985281305"/>
667 <reference key="NSOnImage" ref="271266416"/>
668 <reference key="NSMixedImage" ref="351279908"/>
668 <reference key="NSMixedImage" ref="508123839"/>
669 </object>
669 </object>
670 </object>
670 </object>
671 </object>
671 </object>
@@ -676,8 +676,8 b''
676 <reference key="NSKeyEquiv" ref="255189770"/>
676 <reference key="NSKeyEquiv" ref="255189770"/>
677 <int key="NSKeyEquivModMask">1048576</int>
677 <int key="NSKeyEquivModMask">1048576</int>
678 <int key="NSMnemonicLoc">2147483647</int>
678 <int key="NSMnemonicLoc">2147483647</int>
679 <reference key="NSOnImage" ref="985281305"/>
679 <reference key="NSOnImage" ref="271266416"/>
680 <reference key="NSMixedImage" ref="351279908"/>
680 <reference key="NSMixedImage" ref="508123839"/>
681 <string key="NSAction">submenuAction:</string>
681 <string key="NSAction">submenuAction:</string>
682 <object class="NSMenu" key="NSSubmenu" id="835318025">
682 <object class="NSMenu" key="NSSubmenu" id="835318025">
683 <reference key="NSTitle" ref="64165424"/>
683 <reference key="NSTitle" ref="64165424"/>
@@ -689,8 +689,8 b''
689 <string key="NSKeyEquiv">m</string>
689 <string key="NSKeyEquiv">m</string>
690 <int key="NSKeyEquivModMask">1048576</int>
690 <int key="NSKeyEquivModMask">1048576</int>
691 <int key="NSMnemonicLoc">2147483647</int>
691 <int key="NSMnemonicLoc">2147483647</int>
692 <reference key="NSOnImage" ref="985281305"/>
692 <reference key="NSOnImage" ref="271266416"/>
693 <reference key="NSMixedImage" ref="351279908"/>
693 <reference key="NSMixedImage" ref="508123839"/>
694 </object>
694 </object>
695 <object class="NSMenuItem" id="575023229">
695 <object class="NSMenuItem" id="575023229">
696 <reference key="NSMenu" ref="835318025"/>
696 <reference key="NSMenu" ref="835318025"/>
@@ -698,8 +698,8 b''
698 <reference key="NSKeyEquiv" ref="255189770"/>
698 <reference key="NSKeyEquiv" ref="255189770"/>
699 <int key="NSKeyEquivModMask">1048576</int>
699 <int key="NSKeyEquivModMask">1048576</int>
700 <int key="NSMnemonicLoc">2147483647</int>
700 <int key="NSMnemonicLoc">2147483647</int>
701 <reference key="NSOnImage" ref="985281305"/>
701 <reference key="NSOnImage" ref="271266416"/>
702 <reference key="NSMixedImage" ref="351279908"/>
702 <reference key="NSMixedImage" ref="508123839"/>
703 </object>
703 </object>
704 <object class="NSMenuItem" id="299356726">
704 <object class="NSMenuItem" id="299356726">
705 <reference key="NSMenu" ref="835318025"/>
705 <reference key="NSMenu" ref="835318025"/>
@@ -709,8 +709,8 b''
709 <reference key="NSKeyEquiv" ref="255189770"/>
709 <reference key="NSKeyEquiv" ref="255189770"/>
710 <int key="NSKeyEquivModMask">1048576</int>
710 <int key="NSKeyEquivModMask">1048576</int>
711 <int key="NSMnemonicLoc">2147483647</int>
711 <int key="NSMnemonicLoc">2147483647</int>
712 <reference key="NSOnImage" ref="985281305"/>
712 <reference key="NSOnImage" ref="271266416"/>
713 <reference key="NSMixedImage" ref="351279908"/>
713 <reference key="NSMixedImage" ref="508123839"/>
714 </object>
714 </object>
715 <object class="NSMenuItem" id="625202149">
715 <object class="NSMenuItem" id="625202149">
716 <reference key="NSMenu" ref="835318025"/>
716 <reference key="NSMenu" ref="835318025"/>
@@ -718,8 +718,8 b''
718 <reference key="NSKeyEquiv" ref="255189770"/>
718 <reference key="NSKeyEquiv" ref="255189770"/>
719 <int key="NSKeyEquivModMask">1048576</int>
719 <int key="NSKeyEquivModMask">1048576</int>
720 <int key="NSMnemonicLoc">2147483647</int>
720 <int key="NSMnemonicLoc">2147483647</int>
721 <reference key="NSOnImage" ref="985281305"/>
721 <reference key="NSOnImage" ref="271266416"/>
722 <reference key="NSMixedImage" ref="351279908"/>
722 <reference key="NSMixedImage" ref="508123839"/>
723 </object>
723 </object>
724 </object>
724 </object>
725 <string key="NSName">_NSWindowsMenu</string>
725 <string key="NSName">_NSWindowsMenu</string>
@@ -731,8 +731,8 b''
731 <reference key="NSKeyEquiv" ref="255189770"/>
731 <reference key="NSKeyEquiv" ref="255189770"/>
732 <int key="NSKeyEquivModMask">1048576</int>
732 <int key="NSKeyEquivModMask">1048576</int>
733 <int key="NSMnemonicLoc">2147483647</int>
733 <int key="NSMnemonicLoc">2147483647</int>
734 <reference key="NSOnImage" ref="985281305"/>
734 <reference key="NSOnImage" ref="271266416"/>
735 <reference key="NSMixedImage" ref="351279908"/>
735 <reference key="NSMixedImage" ref="508123839"/>
736 <string key="NSAction">submenuAction:</string>
736 <string key="NSAction">submenuAction:</string>
737 <object class="NSMenu" key="NSSubmenu" id="374024848">
737 <object class="NSMenu" key="NSSubmenu" id="374024848">
738 <reference key="NSTitle" ref="461919786"/>
738 <reference key="NSTitle" ref="461919786"/>
@@ -744,8 +744,8 b''
744 <string key="NSKeyEquiv">?</string>
744 <string key="NSKeyEquiv">?</string>
745 <int key="NSKeyEquivModMask">1048576</int>
745 <int key="NSKeyEquivModMask">1048576</int>
746 <int key="NSMnemonicLoc">2147483647</int>
746 <int key="NSMnemonicLoc">2147483647</int>
747 <reference key="NSOnImage" ref="985281305"/>
747 <reference key="NSOnImage" ref="271266416"/>
748 <reference key="NSMixedImage" ref="351279908"/>
748 <reference key="NSMixedImage" ref="508123839"/>
749 </object>
749 </object>
750 </object>
750 </object>
751 </object>
751 </object>
@@ -860,7 +860,7 b''
860 <bool key="EncodedWithXMLCoder">YES</bool>
860 <bool key="EncodedWithXMLCoder">YES</bool>
861 <object class="NSColor">
861 <object class="NSColor">
862 <int key="NSColorSpace">6</int>
862 <int key="NSColorSpace">6</int>
863 <string key="NSCatalogName" id="945274157">System</string>
863 <string key="NSCatalogName" id="484387293">System</string>
864 <string key="NSColorName">selectedTextBackgroundColor</string>
864 <string key="NSColorName">selectedTextBackgroundColor</string>
865 <object class="NSColor" key="NSColor" id="377165725">
865 <object class="NSColor" key="NSColor" id="377165725">
866 <int key="NSColorSpace">3</int>
866 <int key="NSColorSpace">3</int>
@@ -869,7 +869,7 b''
869 </object>
869 </object>
870 <object class="NSColor">
870 <object class="NSColor">
871 <int key="NSColorSpace">6</int>
871 <int key="NSColorSpace">6</int>
872 <reference key="NSCatalogName" ref="945274157"/>
872 <reference key="NSCatalogName" ref="484387293"/>
873 <string key="NSColorName">selectedTextColor</string>
873 <string key="NSColorName">selectedTextColor</string>
874 <reference key="NSColor" ref="555789289"/>
874 <reference key="NSColor" ref="555789289"/>
875 </object>
875 </object>
@@ -963,13 +963,13 b''
963 <int key="NSCellFlags2">0</int>
963 <int key="NSCellFlags2">0</int>
964 <string key="NSContents">Console</string>
964 <string key="NSContents">Console</string>
965 <object class="NSFont" key="NSSupport" id="26">
965 <object class="NSFont" key="NSSupport" id="26">
966 <string key="NSName" id="257617473">LucidaGrande</string>
966 <string key="NSName" id="378950370">LucidaGrande</string>
967 <double key="NSSize">1.100000e+01</double>
967 <double key="NSSize">1.100000e+01</double>
968 <int key="NSfFlags">3100</int>
968 <int key="NSfFlags">3100</int>
969 </object>
969 </object>
970 <object class="NSColor" key="NSBackgroundColor" id="131515055">
970 <object class="NSColor" key="NSBackgroundColor" id="131515055">
971 <int key="NSColorSpace">6</int>
971 <int key="NSColorSpace">6</int>
972 <reference key="NSCatalogName" ref="945274157"/>
972 <reference key="NSCatalogName" ref="484387293"/>
973 <string key="NSColorName">textBackgroundColor</string>
973 <string key="NSColorName">textBackgroundColor</string>
974 <reference key="NSColor" ref="521347521"/>
974 <reference key="NSColor" ref="521347521"/>
975 </object>
975 </object>
@@ -1043,7 +1043,7 b''
1043 </object>
1043 </object>
1044 <object class="NSColor" key="NSTextColor" id="866628999">
1044 <object class="NSColor" key="NSTextColor" id="866628999">
1045 <int key="NSColorSpace">6</int>
1045 <int key="NSColorSpace">6</int>
1046 <reference key="NSCatalogName" ref="945274157"/>
1046 <reference key="NSCatalogName" ref="484387293"/>
1047 <string key="NSColorName">headerTextColor</string>
1047 <string key="NSColorName">headerTextColor</string>
1048 <reference key="NSColor" ref="555789289"/>
1048 <reference key="NSColor" ref="555789289"/>
1049 </object>
1049 </object>
@@ -1051,22 +1051,22 b''
1051 <object class="NSTextFieldCell" key="NSDataCell" id="525071236">
1051 <object class="NSTextFieldCell" key="NSDataCell" id="525071236">
1052 <int key="NSCellFlags">337772096</int>
1052 <int key="NSCellFlags">337772096</int>
1053 <int key="NSCellFlags2">2048</int>
1053 <int key="NSCellFlags2">2048</int>
1054 <string key="NSContents" id="590184478">Text Cell</string>
1054 <string key="NSContents" id="456204663">Text Cell</string>
1055 <object class="NSFont" key="NSSupport" id="8196371">
1055 <object class="NSFont" key="NSSupport" id="8196371">
1056 <reference key="NSName" ref="257617473"/>
1056 <reference key="NSName" ref="378950370"/>
1057 <double key="NSSize">1.300000e+01</double>
1057 <double key="NSSize">1.300000e+01</double>
1058 <int key="NSfFlags">1044</int>
1058 <int key="NSfFlags">1044</int>
1059 </object>
1059 </object>
1060 <reference key="NSControlView" ref="23853726"/>
1060 <reference key="NSControlView" ref="23853726"/>
1061 <object class="NSColor" key="NSBackgroundColor" id="224028609">
1061 <object class="NSColor" key="NSBackgroundColor" id="224028609">
1062 <int key="NSColorSpace">6</int>
1062 <int key="NSColorSpace">6</int>
1063 <reference key="NSCatalogName" ref="945274157"/>
1063 <reference key="NSCatalogName" ref="484387293"/>
1064 <string key="NSColorName">controlBackgroundColor</string>
1064 <string key="NSColorName">controlBackgroundColor</string>
1065 <reference key="NSColor" ref="377165725"/>
1065 <reference key="NSColor" ref="377165725"/>
1066 </object>
1066 </object>
1067 <object class="NSColor" key="NSTextColor" id="205104690">
1067 <object class="NSColor" key="NSTextColor" id="205104690">
1068 <int key="NSColorSpace">6</int>
1068 <int key="NSColorSpace">6</int>
1069 <reference key="NSCatalogName" ref="945274157"/>
1069 <reference key="NSCatalogName" ref="484387293"/>
1070 <string key="NSColorName">controlTextColor</string>
1070 <string key="NSColorName">controlTextColor</string>
1071 <reference key="NSColor" ref="555789289"/>
1071 <reference key="NSColor" ref="555789289"/>
1072 </object>
1072 </object>
@@ -1091,7 +1091,7 b''
1091 <object class="NSTextFieldCell" key="NSDataCell" id="377147224">
1091 <object class="NSTextFieldCell" key="NSDataCell" id="377147224">
1092 <int key="NSCellFlags">337772096</int>
1092 <int key="NSCellFlags">337772096</int>
1093 <int key="NSCellFlags2">2048</int>
1093 <int key="NSCellFlags2">2048</int>
1094 <reference key="NSContents" ref="590184478"/>
1094 <reference key="NSContents" ref="456204663"/>
1095 <reference key="NSSupport" ref="8196371"/>
1095 <reference key="NSSupport" ref="8196371"/>
1096 <reference key="NSControlView" ref="23853726"/>
1096 <reference key="NSControlView" ref="23853726"/>
1097 <reference key="NSBackgroundColor" ref="224028609"/>
1097 <reference key="NSBackgroundColor" ref="224028609"/>
@@ -1108,7 +1108,7 b''
1108 <reference key="NSBackgroundColor" ref="521347521"/>
1108 <reference key="NSBackgroundColor" ref="521347521"/>
1109 <object class="NSColor" key="NSGridColor">
1109 <object class="NSColor" key="NSGridColor">
1110 <int key="NSColorSpace">6</int>
1110 <int key="NSColorSpace">6</int>
1111 <reference key="NSCatalogName" ref="945274157"/>
1111 <reference key="NSCatalogName" ref="484387293"/>
1112 <string key="NSColorName">gridColor</string>
1112 <string key="NSColorName">gridColor</string>
1113 <object class="NSColor" key="NSColor">
1113 <object class="NSColor" key="NSColor">
1114 <int key="NSColorSpace">3</int>
1114 <int key="NSColorSpace">3</int>
@@ -2787,9 +2787,9 b''
2787 <reference ref="9"/>
2787 <reference ref="9"/>
2788 <reference ref="113577022"/>
2788 <reference ref="113577022"/>
2789 <integer value="0"/>
2789 <integer value="0"/>
2790 <string>{{108, 368}, {725, 337}}</string>
2790 <string>{{27, 368}, {725, 337}}</string>
2791 <reference ref="9"/>
2791 <reference ref="9"/>
2792 <string>{{108, 368}, {725, 337}}</string>
2792 <string>{{27, 368}, {725, 337}}</string>
2793 <reference ref="113577022"/>
2793 <reference ref="113577022"/>
2794 <reference ref="113577022"/>
2794 <reference ref="113577022"/>
2795 <reference ref="113577022"/>
2795 <reference ref="113577022"/>
@@ -2878,8 +2878,8 b''
2878 <object class="NSMutableArray" key="referencedPartialClassDescriptions">
2878 <object class="NSMutableArray" key="referencedPartialClassDescriptions">
2879 <bool key="EncodedWithXMLCoder">YES</bool>
2879 <bool key="EncodedWithXMLCoder">YES</bool>
2880 <object class="IBPartialClassDescription">
2880 <object class="IBPartialClassDescription">
2881 <string key="className">IPython1SandboxAppDelegate</string>
2881 <reference key="className" ref="695797635"/>
2882 <string key="superclassName">NSObject</string>
2882 <nil key="superclassName"/>
2883 <object class="NSMutableDictionary" key="actions">
2883 <object class="NSMutableDictionary" key="actions">
2884 <bool key="EncodedWithXMLCoder">YES</bool>
2884 <bool key="EncodedWithXMLCoder">YES</bool>
2885 <object class="NSArray" key="dict.sortedKeys">
2885 <object class="NSArray" key="dict.sortedKeys">
@@ -2890,17 +2890,17 b''
2890 </object>
2890 </object>
2891 </object>
2891 </object>
2892 <object class="NSMutableDictionary" key="outlets">
2892 <object class="NSMutableDictionary" key="outlets">
2893 <string key="NS.key.0">ipythonController</string>
2893 <reference key="NS.key.0" ref="684042788"/>
2894 <string key="NS.object.0">id</string>
2894 <string key="NS.object.0">NSTextView</string>
2895 </object>
2895 </object>
2896 <object class="IBClassDescriptionSource" key="sourceIdentifier">
2896 <object class="IBClassDescriptionSource" key="sourceIdentifier">
2897 <string key="majorKey">IBProjectSource</string>
2897 <string key="majorKey">IBUserSource</string>
2898 <string key="minorKey">IPython1SandboxAppDelegate.py</string>
2898 <reference key="minorKey" ref="255189770"/>
2899 </object>
2899 </object>
2900 </object>
2900 </object>
2901 <object class="IBPartialClassDescription">
2901 <object class="IBPartialClassDescription">
2902 <reference key="className" ref="695797635"/>
2902 <string key="className">IPython1SandboxAppDelegate</string>
2903 <nil key="superclassName"/>
2903 <string key="superclassName">NSObject</string>
2904 <object class="NSMutableDictionary" key="actions">
2904 <object class="NSMutableDictionary" key="actions">
2905 <bool key="EncodedWithXMLCoder">YES</bool>
2905 <bool key="EncodedWithXMLCoder">YES</bool>
2906 <object class="NSArray" key="dict.sortedKeys">
2906 <object class="NSArray" key="dict.sortedKeys">
@@ -2911,12 +2911,12 b''
2911 </object>
2911 </object>
2912 </object>
2912 </object>
2913 <object class="NSMutableDictionary" key="outlets">
2913 <object class="NSMutableDictionary" key="outlets">
2914 <reference key="NS.key.0" ref="684042788"/>
2914 <string key="NS.key.0">ipythonController</string>
2915 <string key="NS.object.0">NSTextView</string>
2915 <string key="NS.object.0">id</string>
2916 </object>
2916 </object>
2917 <object class="IBClassDescriptionSource" key="sourceIdentifier">
2917 <object class="IBClassDescriptionSource" key="sourceIdentifier">
2918 <string key="majorKey">IBUserSource</string>
2918 <string key="majorKey">IBProjectSource</string>
2919 <reference key="minorKey" ref="255189770"/>
2919 <string key="minorKey">IPython1SandboxAppDelegate.py</string>
2920 </object>
2920 </object>
2921 </object>
2921 </object>
2922 </object>
2922 </object>
@@ -2932,18 +2932,18 b' AQUBBgEHAQgBCQEKAQsBDwEQARkBIQEmASoBLQExATUBOQE7AT0BTQFSAVUBWgFBAVQBYwFqAWsBbAFv'
2932 AXQBdQF4AYAAkAGBAYQBhwGIAYkBjgGPAZABkwGYAZkBmwGeAasBrAGtAbEBvAG9Ab4BwQHCAcQBxQHG
2932 AXQBdQF4AYAAkAGBAYQBhwGIAYkBjgGPAZABkwGYAZkBmwGeAasBrAGtAbEBvAG9Ab4BwQHCAcQBxQHG
2933 AdIB0wHbAdwB3wHkAeUB6AHtAfAB/AIAAgcCCwIdAiUCLwIzAlECUgJaAmQCZQJoAm4CbwJyAncCiAKP
2933 AdIB0wHbAdwB3wHkAeUB6AHtAfAB/AIAAgcCCwIdAiUCLwIzAlECUgJaAmQCZQJoAm4CbwJyAncCiAKP
2934 ApACkwKYApkCnAKmAqcCrAKxArICtwK4ArsCwwLJAsoC0QLWAtcC2gLcAt0C5gLnAvAC8QL1AvYC9wL4
2934 ApACkwKYApkCnAKmAqcCrAKxArICtwK4ArsCwwLJAsoC0QLWAtcC2gLcAt0C5gLnAvAC8QL1AvYC9wL4
2935 AvkC/wMAAwIDAwMEAwcDFgMYAxsDHAMfAAsDIAMhAyIDJQNXA10DbgNzA3QDdQN6A3sDfAN/A4MDhAOH
2935 AvkC/wMAAwIDAwMEAwcDFgMYAxsDHAMfAAsDIAMhAyIDJQNXA10DbQNzASkDdAN5A3oDewN+A4IDgwOG
2936 A4gDjAOQA5cDmwOcA50DngOiA6kDqgOrA6wDsAO3A7sDvAO9A74DwgPJA80DzgPPA9AD1APcA90D3gPf
2936 A4cDiwOPA5YDmgObA5wDnQOhA6gDrAOtA64DrwOzA7wDwAPBA8IDwwPHA84D0gPTA9QD1QPZA+AD5APl
2937 A+MD6wPwA/ED8gPzA/cD/gQCBAMEBAQFBAkEEAQRBBIEEwQXBB4EHwQgBCQEKwQvASkEMAQxBDcEOgQ7
2937 A+YD6gPxA/UD9gP3A/gD/AQDBAQEBQQJBBAEEQQSBBMEFwQeBB8EIAQkBCsELwQwBDEENQQ9BD4EPwRA
2938 BDwEPwRDBEoESwRMBFAEVwRcBF0EXgRfBGMEagRrBGwEcAR3BHgEeQR9BIQEhQSGBIcEjASTBJQElQSZ
2938 BEQESwRMBE0ETgRUBFcEWgRbBFwEXwRjBGoEawRsBG0EcgR1BHYEdwR7BIIEgwSEBIUEiQSQBJUElgSX
2939 BKAEpASlBKYEpwSrBLIEswS0BLUEuQTABMEEwgTGBM0EzgTPBNME2gTbBNwE3QThBOgE6QTqBOsE7wT2
2939 BJgEnASjBKQEpQSmBKoEsQSyBLMEtAS4BL8EwwTEBMUExgTKBNEE0gTTBNQE2ATfBOAE4QTiBOYE7QTx
2940 BPcE+AT5BP0FBAUFBQYFBwUMBQ8FEAURBRUFHAUdBR4FIgUpBSoFKwUsBTAFNwU7BTwFPQU+BUMFRgVK
2940 BPIE8wT0BPgE/wUABQEFBgUNBQ4FDwUTBRwFHQUeBR8FJAUoBS8FMAUxBTIFNwU4BTwFQwVEBUUFRgVK
2941 BVEFUgVTBVcFXgViBWMFZAVlBWkFcAV1BXYFdwV7BYIFgwWEBYgFjwWQBZEFkgWXBZgFnQWeBaIFqwWs
2941 BVEFVgVXBVgFXAVjBWQFZQVpBXAFcQVyBXcFeAV8BYMFhAWFBYkFkAWRBZIFlgWdBZ4FnwWjBaoFqwWs
2942 Ba0FrgWyBbkFugW7BbwFwAXHBcgFyQXNBdQF1QXWBeAF9gX8Bf0F/gX/BgMGCwYMBg8GEQYXBhgGGQYc
2942 BbAFtwW4BbkFugW+BcUFxgXHBcgFzAXTBdQF1QXWBeAF9gX8Bf0F/gX/BgMGCwYMBg8GEQYXBhgGGQYa
2943 BiMGJAYlBiYGLQYuBi8GNgY3BjgGOQZABkEGQgZDBq0Gtwa4BrkGvgbABskGuAbKBs4GzwbYBrgG2Qbf
2943 Bh0GJAYlBiYGJwYuBi8GMAY3BjgGOQZABkEGQgZDBq0GuAbCBscGyAbJBs4G1QbWBtgG2QbdBt4GyAbn
2944 BuQG5QbvBvgGuAb5BwcHEgcZBxoHGwckBy0GuAcuBzMHNgc3B0AHSQdKB1MGuAdUB2IHaQdqB2sHcgdz
2944 BvAGyAbxBvgHAQbIBwIHEgcbByQHLQbIBy4HNgc9Bz4HRQdGB04HTwdQB1kGyAdaB2AHaQbIB2oHbwdw
2945 B3QHfQeGB48GuAeQB6AHqQeyB7sGuAe8B8QHywfMB9MH1AfcB90H3gfnBrgH6AfvB/gGuAf5B/4IBQgG
2945 B3oHgwbIB4QHkgebB6IHowekB60HtgbIB7cHvAe/B8AHyQfKB9MGyAfUB+IH6QfqB+sH8gfzB/QH/QgG
2946 CA8GuAgQCBUIHga4CB8IJggvCDAIOQa4CDoIPgg/CKkJFAl/CYAJgQmCCYMJhAmFCYYJhwmICYkJigmL
2946 CA8GyAgQCBUIHgbICB8IJggvCDAIOQbICDoIPgg/CKkJFAl/CYAJgQmCCYMJhAmFCYYJhwmICYkJigmL
2947 CYwJjQmOCY8JkAmRCZIJkwmUCZUJlgmXCZgJmQmaCZsJnAmdCZ4JnwmgCaEJogmjCaQJpQmmCacJqAmp
2947 CYwJjQmOCY8JkAmRCZIJkwmUCZUJlgmXCZgJmQmaCZsJnAmdCZ4JnwmgCaEJogmjCaQJpQmmCacJqAmp
2948 CaoJqwmsCa0JrgmvCbAJsQmyCbMJtAm1CbYJtwm4CbkJugm7CbwJvQm+Cb8JwAnBCcIJwwnECcUJxgnH
2948 CaoJqwmsCa0JrgmvCbAJsQmyCbMJtAm1CbYJtwm4CbkJugm7CbwJvQm+Cb8JwAnBCcIJwwnECcUJxgnH
2949 CcgJyQnKCcsJzAnNCc4JzwnQCdEJ0gnTCdQJ1QnWCdcJ2AnZCdoJ2wncCd0J3gnfCeAJ4QniCeMJ5Anl
2949 CcgJyQnKCcsJzAnNCc4JzwnQCdEJ0gnTCdQJ1QnWCdcJ2AnZCdoJ2wncCd0J3gnfCeAJ4QniCeMJ5Anl
@@ -3073,351 +3073,352 b' ezE2LCAxNn190gA3ADgDHQMepAMeAYwBjQA7XxATTlNQcm9ncmVzc0luZGljYXRvclp7NzI1LCAzMzd9'
3073 XxAVe3swLCAwfSwgezEyODAsIDc3OH19XxAQaXB5dGhvbjFfc2FuZGJveNIANwA4AyMDJKIDJAA7XxAQ
3073 XxAVe3swLCAwfSwgezEyODAsIDc3OH19XxAQaXB5dGhvbjFfc2FuZGJveNIANwA4AyMDJKIDJAA7XxAQ
3074 TlNXaW5kb3dUZW1wbGF0ZdIADgA+AGkDJ4A0rxAvAygDKQMqAysDLAMtAy4DLwMwAzEDMgMzAzQDNQM2
3074 TlNXaW5kb3dUZW1wbGF0ZdIADgA+AGkDJ4A0rxAvAygDKQMqAysDLAMtAy4DLwMwAzEDMgMzAzQDNQM2
3075 AzcDOAM5AzoDOwM8Az0DPgM/A0ADQQNCA0MDRANFA0YDRwNIA0kDSgNLA0wDTQNOA08DUANRA1IDUwNU
3075 AzcDOAM5AzoDOwM8Az0DPgM/A0ADQQNCA0MDRANFA0YDRwNIA0kDSgNLA0wDTQNOA08DUANRA1IDUwNU
3076 A1UDVoCugLyAwoDHgM2A04DYgN6A5IDpgO2A84D4gPyBAQKBAQaBAQqBAQ+BAROBARmBAR6BASKBASaB
3076 A1UDVoCugLyAwoDIgM6A1IDZgN+A44DogOyA8YD2gPuBAQGBAQaBAQqBAQ+BARWBARqBAR+BASWBASqB
3077 ASuBATCBATWBATqBAT6BAUKBAUeBAU2BAU+BAVOBAVmBAV6BAWKBAWeBAWmBAWuBAXCBAXWBAXmBAX2B
3077 AS+BATWBATmBAT2BAUKBAUOBAUiBAUqBAU+BAVSBAViBAVyBAV6BAWKBAWaBAWqBAW6BAXOBAXiBAX2B
3078 AYyBAZCBAZOBAZfTAA4DWANZA1oDWwNcWE5TU291cmNlV05TTGFiZWyAu4CvgLrZAA4DXgNfA2ADYQNi
3078 AY2BAZGBAZSBAZfTAA4DWANZA1oDWwNcWE5TU291cmNlV05TTGFiZWyAu4CvgLrYAA4DXgNfA2ADYQNi
3079 A2MDZANlA2YDZwNoA2kDagNrA2wDbQBVV05TVGl0bGVfEBFOU0tleUVxdWl2TW9kTWFza1pOU0tleUVx
3079 A2MDZANlA2YDZwNoA2kDagNrA2xXTlNUaXRsZV8QEU5TS2V5RXF1aXZNb2RNYXNrWk5TS2V5RXF1aXZd
3080 dWl2XU5TTW5lbW9uaWNMb2NZTlNPbkltYWdlXE5TTWl4ZWRJbWFnZVZOU01lbnVVTlNUYWeAuYCxEgAQ
3080 TlNNbmVtb25pY0xvY1lOU09uSW1hZ2VcTlNNaXhlZEltYWdlVk5TTWVudYC5gLESABAAAICyEn////+A
3081 AACAshJ/////gLOAt4Cw0wAOA14DbwNwA3EDcltOU01lbnVJdGVtc4EBoIEBp4EBqVxTbWFydCBRdW90
3081 s4C3gLDUAA4DXgHVA24DbwNwA3EDcltOU01lbnVJdGVtc4EBpIEByoEB2YEBzFhTaG93IEFsbNMADgAy
3082 ZXNRZ9MADgAyA3YDdwN4A3leTlNSZXNvdXJjZU5hbWWAtoC0gLVXTlNJbWFnZV8QD05TTWVudUNoZWNr
3082 A3UDdgN3A3heTlNSZXNvdXJjZU5hbWWAtoC0gLVXTlNJbWFnZV8QD05TTWVudUNoZWNrbWFya9IANwA4
3083 bWFya9IANwA4A30DfqIDfgA7XxAQTlNDdXN0b21SZXNvdXJjZdMADgAyA3YDdwN4A4KAtoC0gLhfEBBO
3083 A3wDfaIDfQA7XxAQTlNDdXN0b21SZXNvdXJjZdMADgAyA3UDdgN3A4GAtoC0gLhfEBBOU01lbnVNaXhl
3084 U01lbnVNaXhlZFN0YXRl0gA3ADgDhQOGogOGADtaTlNNZW51SXRlbV8QIXRvZ2dsZUF1dG9tYXRpY1F1
3084 ZFN0YXRl0gA3ADgDhAOFogOFADtaTlNNZW51SXRlbV8QFnVuaGlkZUFsbEFwcGxpY2F0aW9uczrSADcA
3085 b3RlU3Vic3RpdHV0aW9uOtIANwA4A4kDiqMDigOLADtfEBVOU05pYkNvbnRyb2xDb25uZWN0b3JeTlNO
3085 OAOIA4mjA4kDigA7XxAVTlNOaWJDb250cm9sQ29ubmVjdG9yXk5TTmliQ29ubmVjdG9y0wAOA1gDWQNa
3086 aWJDb25uZWN0b3LTAA4DWANZA1oDjgOPgLuAvYDB2AAOA14DXwNgA2EDYgNjA2QDZgOSA2gDkwNqA2sD
3086 A40DjoC7gL2AwdgADgNeA18DYANhA2IDYwNkA2UDkQNnA5IDaQNqA2sDlYC5gL+AwICzgLeAvtMADgNe
3087 bAOWgLmAv4DAgLOAt4C+0wAOA14DbwNwA5kDmoEBoIEB0oEB1F8QEUp1bXAgdG8gU2VsZWN0aW9uUWpf
3087 A24DbwOYA5mBAaSBAauBAa1eQ2hlY2sgU3BlbGxpbmdRO15jaGVja1NwZWxsaW5nOtMADgNYA1kDWgOf
3088 EB1jZW50ZXJTZWxlY3Rpb25JblZpc2libGVBcmVhOtMADgNYA1kDWgOgA6GAu4DDgMbZAA4DXgNfA2AD
3088 A6CAu4DDgMfYAA4DXgNfA2ADYQNiA2MDZANlA6MDZwOkA2kDagNrA6eAuYDFgMaAs4C3gMTTAA4DXgNu
3089 YQNiA2MDZANlA2YDpANoA6UDagNrA2wDbQCQgLmAxIDFgLOAt4CwXxAQU21hcnQgQ29weS9QYXN0ZVFm
3089 A28DqgOrgQGkgQHbgQHdZgBQAHIAaQBuAHQgJlFwVnByaW50OtMADgNYA1kDWgOxA7KAu4DJgM3ZAA4D
3090 XxAYdG9nZ2xlU21hcnRJbnNlcnREZWxldGU60wAOA1gDWQNaA64Dr4C7gMiAzNgADgNeA18DYANhA2ID
3090 XgNfA2ADYQNiA2MDZAO0A2UDtgO3A7gDaQNqA2sDuwFYVU5TVGFngLmAyxIAEgAAgMyAs4C3gMrTAA4D
3091 YwNkA2YDsgNoA7MDagNrA2wDtoC5gMqAy4CzgLeAydMADgNeA28DcAO5A7qBAaCBAcCBAcJUU2F2ZVFz
3091 XgNuA28DvgO/gQGkgQHAgQHCW1NtYXJ0IExpbmtzUUdfEB10b2dnbGVBdXRvbWF0aWNMaW5rRGV0ZWN0
3092 XXNhdmVEb2N1bWVudDrTAA4DWANZA1oDwAPBgLuAzoDS2AAOA14DXwNgA2EDYgNjA2QDZgPEA2gDxQNq
3092 aW9uOtMADgNYA1kDWgPFA8aAu4DPgNPYAA4DXgNfA2ADYQNiA2MDZANlA8kDtwPKA2kDagNrA82AuYDR
3093 A2sDbAPIgLmA0IDRgLOAt4DP0wAOA14DbwNwA8sDzIEBoIEBzIEBzlRVbmRvUXpVdW5kbzrTAA4DWANZ
3093 gNKAs4C3gNDTAA4DXgNuA28D0APRgQGkgQGvgQGxVFJlZG9RWlVyZWRvOtMADgNYA1kDWgPXA9iAu4DV
3094 A1oD0gPTgLuA1IDX2AAOA14DXwNgA2EDYgNjA2QDZgPWA9cD2ANqA2sDbAPIgLmA1RIAEgAAgNaAs4C3
3094 gNjYAA4DXgNfA2ADYQNiA2MDZANlA9sDZwNoA2kDagNrA9+AuYDXgLKAs4C3gNbTAA4DXgNuA28D4gPj
3095 gM9UUmVkb1FaVXJlZG860wAOA1gDWQNaA+ED4oC7gNmA3dgADgNeA18DYANhA2IDYwNkA2YD5QPmA+cD
3095 gQGkgQHEgQHGXlN0YXJ0IFNwZWFraW5nXnN0YXJ0U3BlYWtpbmc60wAOA1gDWQNaA+gD6YC7gNqA3tgA
3096 agNrA2wD6oC5gNsSABgAAIDcgLOAt4Da1AAOA14B1QNvA3AD7QPuA++BAaCBAa6BAb6BAbBbSGlkZSBP
3096 DgNeA18DYANhA2IDYwNkA2UD7ANnA+0DaQNqA2sD8IC5gNyA3YCzgLeA29MADgNeA24DbwPzA/SBAaSB
3097 dGhlcnNRaF8QFmhpZGVPdGhlckFwcGxpY2F0aW9uczrTAA4DWANZA1oD9QP2gLuA34Dj2AAOA14DXwNg
3097 AfGBAfNfEBRJUHl0aG9uMVNhbmRib3ggSGVscFE/WXNob3dIZWxwOtMADgNYA1kDWgP6A/uAu4DggOLY
3098 A2EDYgNjA2QDZgP5A9cD+gNqA2sDbAP9gLmA4YDigLOAt4Dg0wAOA14DbwNwBAAEAYEBoIEB4YEB41tT
3098 AA4DXgNfA2ADYQNiA2MDZANlA/4DZwNoA2kDagNrA5WAuYDhgLKAs4C3gL5fEBtDaGVjayBTcGVsbGlu
3099 aG93IENvbG9yc1FDXxAVb3JkZXJGcm9udENvbG9yUGFuZWw60wAOA1gDWQNaBAcECIC7gOWA6NgADgNe
3099 ZyBXaGlsZSBUeXBpbmdfEB50b2dnbGVDb250aW51b3VzU3BlbGxDaGVja2luZzrTAA4DWANZA1oEBwQI
3100 A18DYANhA2IDYwNkA2YECwNoBAwDagNrA2wDyIC5gOaA54CzgLeAz1VQYXN0ZVF2VnBhc3RlOtMADgNY
3100 gLuA5IDn2AAOA14DXwNgA2EDYgNjA2QDZQQLA2cEDANpA2oDawPNgLmA5YDmgLOAt4DQWlNlbGVjdCBB
3101 A1kDWgQVBBaAu4DqgOzZAA4DXgNfA2ADYQNiA2MDZANlA2YEGQNoA6UDagNrA2wDlgCQgLmA64DFgLOA
3101 bGxRYVpzZWxlY3RBbGw60wAOA1gDWQNaBBUEFoC7gOmA69gADgNeA18DYANhA2IDYwNkA2UEGQNnA2gD
3102 t4C+ZQBGAGkAbgBkICZfEBdwZXJmb3JtRmluZFBhbmVsQWN0aW9uOtMADgNYA1kDWgQiBCOAu4DugPLY
3102 aQNqA2sDlYC5gOqAsoCzgLeAvl8QG0NoZWNrIEdyYW1tYXIgV2l0aCBTcGVsbGluZ18QFnRvZ2dsZUdy
3103 AA4DXgNfA2ADYQNiA2MDZANmBCYDaAQnA2oDawNsBCqAuYDwgPGAs4C3gO/TAA4DXgNvA3AELQQugQGg
3103 YW1tYXJDaGVja2luZzrTAA4DWANZA1oEIgQjgLuA7YDw2AAOA14DXwNgA2EDYgNjA2QDZQQmA2cDaANp
3104 gQGdgQGfXVN0b3AgU3BlYWtpbmddc3RvcFNwZWFraW5nOtQADgQyA1gDWQQzBDQAQQQ2XU5TRGVzdGlu
3104 A2oDawQqgLmA74CygLOAt4Du0wAOA14DbgNvBC0ELoEBpIEB54EB6W8QEgBDAHUAcwB0AG8AbQBpAHoA
3105 YXRpb26A94D0gAeA9tIADgAyADMEOYAEgPVfEBZJUHl0aG9uQ29jb2FDb250cm9sbGVyWGRlbGVnYXRl
3105 ZQAgAFQAbwBvAGwAYgBhAHIgJl8QH3J1blRvb2xiYXJDdXN0b21pemF0aW9uUGFsZXR0ZTrTAA4DWANZ
3106 0gA3ADgEPQQ+owQ+A4sAO18QFE5TTmliT3V0bGV0Q29ubmVjdG9y0wAOA1gDWQNaBEEEQoC7gPmA+9gA
3106 A1oEMwQ0gLuA8oD12AAOA14DXwNgA2EDYgNjA2QDZQQ3BDgEOQNpA2oDawQqgLmA8xIAGAAAgPSAs4C3
3107 DgNeA18DYANhA2IDYwNkA2YERQNoBCcDagNrA2wDyIC5gPqA8YCzgLeAz1ZEZWxldGVXZGVsZXRlOtMA
3107 gO5cU2hvdyBUb29sYmFyUXRfEBN0b2dnbGVUb29sYmFyU2hvd2460wAOA1gDWQNaBEIEQ4C7gPeA+tgA
3108 DgNYA1kDWgROBE+Au4D9gQEB2AAOA14DXwNgA2EDYgNjA2QDZgRSA2gEUwNqA2sDbARWgLmA/4EBAICz
3108 DgNeA18DYANhA2IDYwNkA2UERgNnBEcDaQNqA2sDp4C5gPiA+YCzgLeAxFRTYXZlUXNdc2F2ZURvY3Vt
3109 gLeA/tQADgNeAdUDbwNwBFkEWgRbgQGggQHrgQHvgQHtWE1pbmltaXplUW1fEBNwZXJmb3JtTWluaWF0
3109 ZW50OtQADgRPA1gDWQRQBFEEUgRTXU5TRGVzdGluYXRpb26BAQCA/YD8gP/SAA4AMgAzADSABIAD0gAO
3110 dXJpemU60wAOA1gDWQNaBGEEYoC7gQEDgQEF2AAOA14DXwNgA2EDYgNjA2QDZgRlA2gEJwNqA2sDbARW
3110 ADIAMwRZgASA/l8QGklQeXRob24xU2FuZGJveEFwcERlbGVnYXRlWGRlbGVnYXRl0gA3ADgEXQReowRe
3111 gLmBAQSA8YCzgLeA/l8QEkJyaW5nIEFsbCB0byBGcm9udF8QD2FycmFuZ2VJbkZyb250OtMADgNYA1kD
3111 A4oAO18QFE5TTmliT3V0bGV0Q29ubmVjdG9y0wAOA1gDWQNaBGEEYoC7gQECgQEF2QAOA14DXwNgA2ED
3112 WgRuBG+Au4EBB4EBCdgADgNeA18DYANhA2IDYwNkA2YEcgNoBCcDagNrA2wD6oC5gQEIgPGAs4C3gNpY
3112 YgNjA2QDtANlBGUDZwRmA2kDagNrA7sAVYC5gQEDgQEEgLOAt4DKXFNtYXJ0IFF1b3Rlc1FnXxAhdG9n
3113 U2hvdyBBbGxfEBZ1bmhpZGVBbGxBcHBsaWNhdGlvbnM60wAOA1gDWQNaBHsEfIC7gQELgQEO2AAOA14D
3113 Z2xlQXV0b21hdGljUXVvdGVTdWJzdGl0dXRpb2461AAOBE8DWANZBFAEbwRRBHGBAQCBAQeA/YEBCdIA
3114 XwNgA2EDYgNjA2QDZgR/A2gEgANqA2sDbAO2gLmBAQyBAQ2As4C3gMlVQ2xvc2VRd11wZXJmb3JtQ2xv
3114 DgAyADMEdIAEgQEIXxAWSVB5dGhvbkNvY29hQ29udHJvbGxlcl8QEWlweXRob25Db250cm9sbGVy0wAO
3115 c2U61AAOBDIDWANZA1oAHwSKBIuAu4ACgQEQgQES1wAOA14DYANhA2IDYwNkA2YEjgQnA2oDawNsA+qA
3115 A1gDWQNaBHkEeoC7gQELgQEO2AAOA14DXwNgA2EDYgNjA2QDZQR9A2cEfgNpA2oDawNsgLmBAQyBAQ2A
3116 uYEBEYDxgLOAt4DaXxAVQWJvdXQgSVB5dGhvbjFTYW5kYm94XxAdb3JkZXJGcm9udFN0YW5kYXJkQWJv
3116 s4C3gLBfEBRRdWl0IElQeXRob24xU2FuZGJveFFxWnRlcm1pbmF0ZTrTAA4DWANZA1oEhwSIgLuBARCB
3117 dXRQYW5lbDrTAA4DWANZA1oElwSYgLuBARSBARjYAA4DXgNfA2ADYQNiA2MDZANmBJsDaAScA2oDawNs
3117 ARTYAA4DXgNfA2ADYQNiA2MDZANlBIsDZwSMA2kDagNrBI+AuYEBEoEBE4CzgLeBARHUAA4DXgHVA24D
3118 BJ+AuYEBFoEBF4CzgLeBARXTAA4DXgNvA3AEogSjgQGggQHdgQHfXkNoZWNrIFNwZWxsaW5nUTteY2hl
3118 bwSSBJMElIEBpIEB64EB74EB7VhNaW5pbWl6ZVFtXxATcGVyZm9ybU1pbmlhdHVyaXplOtMADgNYA1kD
3119 Y2tTcGVsbGluZzrTAA4DWANZA1oEqQSqgLuBARqBAR3YAA4DXgNfA2ADYQNiA2MDZANmBK0DaASuA2oD
3119 WgSaBJuAu4EBFoEBGdgADgNeA18DYANhA2IDYwNkA2UEngNnBJ8DaQNqA2sDzYC5gQEXgQEYgLOAt4DQ
3120 awNsA8iAuYEBG4EBHICzgLeAz1pTZWxlY3QgQWxsUWFac2VsZWN0QWxsOtMADgNYA1kDWgS3BLiAu4EB
3120 VFVuZG9RelV1bmRvOtMADgNYA1kDWgSoBKmAu4EBG4EBHtgADgNeA18DYANhA2IDYwNkA2UErANnBK0D
3121 H4EBIdgADgNeA18DYANhA2IDYwNkA2YEuwNoA+cDagNrA2wD6oC5gQEggNyAs4C3gNpfEBRIaWRlIElQ
3121 aQNqA2sDlYC5gQEcgQEdgLOAt4C+bgBTAGgAbwB3ACAAUwBwAGUAbABsAGkAbgBnICZROl8QD3Nob3dH
3122 eXRob24xU2FuZGJveFVoaWRlOtMADgNYA1kDWgTEBMWAu4EBI4EBJdgADgNeA18DYANhA2IDYwNkA2YE
3122 dWVzc1BhbmVsOtMADgNYA1kDWgS2BLeAu4EBIIEBJNgADgNeA18DYANhA2IDYwNkA2UEugO3BLsDaQNq
3123 yANoBCcDagNrA2wEn4C5gQEkgPGAs4C3gQEVXxAbQ2hlY2sgU3BlbGxpbmcgV2hpbGUgVHlwaW5nXxAe
3123 A2sEvoC5gQEigQEjgLOAt4EBIdMADgNeA24DbwTBBMKBAaSBAZ+BAaFbU2hvdyBDb2xvcnNRQ18QFW9y
3124 dG9nZ2xlQ29udGludW91c1NwZWxsQ2hlY2tpbmc60wAOA1gDWQNaBNEE0oC7gQEngQEq2AAOA14DXwNg
3124 ZGVyRnJvbnRDb2xvclBhbmVsOtMADgNYA1kDWgTIBMmAu4EBJoEBKdgADgNeA18DYANhA2IDYwNkA2UE
3125 A2EDYgNjA2QDZgTVA2gE1gNqA2sDbAO2gLmBASiBASmAs4C3gMlmAFAAcgBpAG4AdCAmUXBWcHJpbnQ6
3125 zAQ4BM0DaQNqA2sDbIC5gQEngQEogLOAt4CwW0hpZGUgT3RoZXJzUWhfEBZoaWRlT3RoZXJBcHBsaWNh
3126 0wAOA1gDWQNaBN8E4IC7gQEsgQEv2AAOA14DXwNgA2EDYgNjA2QDZgTjA9cE5ANqA2sDbAO2gLmBAS2B
3126 dGlvbnM60wAOA1gDWQNaBNYE14C7gQErgQEu2AAOA14DXwNgA2EDYgNjA2QDZQTaA2cE2wNpA2oDawPN
3127 AS6As4C3gMloAFMAYQB2AGUAIABBAHMgJlFTXxAPc2F2ZURvY3VtZW50QXM60wAOA1gDWQNaBO0E7oC7
3127 gLmBASyBAS2As4C3gNBUQ29weVFjVWNvcHk60wAOA1gDWQNaBOQE5YC7gQEwgQE02QAOA14DXwNgA2ED
3128 gQExgQE02AAOA14DXwNgA2EDYgNjA2QDZgTxA2gE8gNqA2sDbAPqgLmBATKBATOAs4C3gNpfEBRRdWl0
3128 YgNjA2QDtANlBOgDZwTpA2kDagNrBOwAkIC5gQEygQEzgLOAt4EBMdMADgNeA24DbwTvBPCBAaSBAbWB
3129 IElQeXRob24xU2FuZGJveFFxWnRlcm1pbmF0ZTrTAA4DWANZA1oE+wT8gLuBATaBATnZAA4DXgNfA2AD
3129 AbdlAEYAaQBuAGQgJlFmXxAXcGVyZm9ybUZpbmRQYW5lbEFjdGlvbjrTAA4DWANZA1oE9gT3gLuBATaB
3130 YQNiA2MDZANlA2YE/wPXBQADagNrA2wDbQFYgLmBATeBATiAs4C3gLBbU21hcnQgTGlua3NRR18QHXRv
3130 ATjYAA4DXgNfA2ADYQNiA2MDZANlBPoDZwNoA2kDagNrA9+AuYEBN4CygLOAt4DWXVN0b3AgU3BlYWtp
3131 Z2dsZUF1dG9tYXRpY0xpbmtEZXRlY3Rpb2461AAOBDIDWANZBDMENAUKBQuA94D0gQE7gQE90gAOADIA
3131 bmddc3RvcFNwZWFraW5nOtQADgRPA1gDWQNaAB8FBAUFgLuAAoEBOoEBPNcADgNeA2ADYQNiA2MDZANl
3132 MwUOgASBATxfEBpJUHl0aG9uMVNhbmRib3hBcHBEZWxlZ2F0ZV8QEWlweXRob25Db250cm9sbGVy0wAO
3132 BQgDaANpA2oDawNsgLmBATuAsoCzgLeAsF8QFUFib3V0IElQeXRob24xU2FuZGJveF8QHW9yZGVyRnJv
3133 A1gDWQNaBRMFFIC7gQE/gQFB2AAOA14DXwNgA2EDYgNjA2QDZgUXA2gEJwNqA2sDbARWgLmBAUCA8YCz
3133 bnRTdGFuZGFyZEFib3V0UGFuZWw60wAOA1gDWQNaBREFEoC7gQE+gQFB2QAOBRQDXgNfA2ADYQNiA2MD
3134 gLeA/lRab29tXHBlcmZvcm1ab29tOtMADgNYA1kDWgUgBSGAu4EBQ4EBRtgADgNeA18DYANhA2IDYwNk
3134 ZANlA2gFFwO3BRgDaQNqA2sDp1lOU1Rvb2xUaXCAuYCygQE/gQFAgLOAt4DEXVBhZ2UgU2V0dXAuLi5R
3135 A2YFJANoBSUDagNrA2wDyIC5gQFEgQFFgLOAt4DPVENvcHlRY1Vjb3B5OtMADgNYA1kDWgUuBS+Au4EB
3135 UF5ydW5QYWdlTGF5b3V0OtQADgRPA1gDWQRQBG8AQQRTgQEAgQEHgAeA/9MADgNYA1kDWgUmBSeAu4EB
3136 SIEBTNgADgNeA18DYANhA2IDYwNkA2YFMgPmBTMDagNrA2wFNoC5gQFKgQFLgLOAt4EBSdMADgNeA28D
3136 RIEBR9gADgNeA18DYANhA2IDYwNkA2UFKgNnBSsDaQNqA2sDzYC5gQFFgQFGgLOAt4DQVVBhc3RlUXZW
3137 cAU5BTqBAaCBAeeBAelcU2hvdyBUb29sYmFyUXRfEBN0b2dnbGVUb29sYmFyU2hvd2461AAOBDIDWANZ
3137 cGFzdGU61AAOBE8DWANZBFAAyABBBTaBAQCAGIAHgQFJXxAVaW5pdGlhbEZpcnN0UmVzcG9uZGVy0wAO
3138 BDMFCgVBBDaA94EBO4EBToD20gAOADIAMwA0gASAA9MADgNYA1kDWgVIBUmAu4EBUIEBUtgADgNeA18D
3138 A1gDWQNaBToFO4C7gQFLgQFO2AAOA14DXwNgA2EDYgNjA2QDZQU+A2cFPwNpA2oDawTsgLmBAUyBAU2A
3139 YANhA2IDYwNkA2YFTANoBCcDagNrA2wEKoC5gQFRgPGAs4C3gO9eU3RhcnQgU3BlYWtpbmdec3RhcnRT
3139 s4C3gQExXxARSnVtcCB0byBTZWxlY3Rpb25Ral8QHWNlbnRlclNlbGVjdGlvbkluVmlzaWJsZUFyZWE6
3140 cGVha2luZzrTAA4DWANZA1oFVQVWgLuBAVSBAVjYAA4DXgNfA2ADYQNiA2MDZANmBVkDaAVaA2oDawNs
3140 0wAOA1gDWQNaBUgFSYC7gQFQgQFT2AAOA14DXwNgA2EDYgNjA2QDZQVMA2cDaANpA2oDawVQgLmBAVKA
3141 BV2AuYEBVoEBV4CzgLeBAVXTAA4DXgNvA3AFYAVhgQGggQHxgQHzXxAUSVB5dGhvbjFTYW5kYm94IEhl
3141 soCzgLeBAVHUAA4DXgHVA24DbwVTBVQFVYEBpIEBpYEBp4EBplpDbGVhciBNZW51XxAVY2xlYXJSZWNl
3142 bHBRP1lzaG93SGVscDrTAA4DWANZA1oFZwVogLuBAVqBAV3YAA4DXgNfA2ADYQNiA2MDZANmBWsDaAQn
3142 bnREb2N1bWVudHM60wAOA1gDWQNaBVoFW4C7gQFVgQFX2AAOA14DXwNgA2EDYgNjA2QDZQVeA2cDaANp
3143 A2oDawNsBW+AuYEBXIDxgLOAt4EBW9QADgNeAdUDbwNwBXIFcwV0gQGggQGigQGlgQGkWkNsZWFyIE1l
3143 A2oDawPNgLmBAVaAsoCzgLeA0FZEZWxldGVXZGVsZXRlOtMADgNYA1kDWgVnBWiAu4EBWYEBW9cADgNe
3144 bnVfEBVjbGVhclJlY2VudERvY3VtZW50czrTAA4DWANZA1oFeQV6gLuBAV+BAWHYAA4DXgNfA2ADYQNi
3144 A2ADYQNiA2MDZANlBWsDaANpA2oDawOngLmBAVqAsoCzgLeAxF8QD1JldmVydCB0byBTYXZlZF8QFnJl
3145 A2MDZANmBX0DaAQnA2oDawNsBTaAuYEBYIDxgLOAt4EBSW8QEgBDAHUAcwB0AG8AbQBpAHoAZQAgAFQA
3145 dmVydERvY3VtZW50VG9TYXZlZDrUAA4ETwNYA1kEUADIBG8FdoEBAIAYgQEHgQFdWHRleHRWaWV30wAO
3146 bwBvAGwAYgBhAHIgJl8QH3J1blRvb2xiYXJDdXN0b21pemF0aW9uUGFsZXR0ZTrTAA4DWANZA1oFhgWH
3146 A1gDWQNaBXoFe4C7gQFfgQFh2AAOA14DXwNgA2EDYgNjA2QDZQV+A2cDaANpA2oDawSPgLmBAWCAsoCz
3147 gLuBAWOBAWbYAA4DXgNfA2ADYQNiA2MDZANmBYoDaAWLA2oDawNsA8iAuYEBZIEBZYCzgLeAz1NDdXRR
3147 gLeBARFfEBJCcmluZyBBbGwgdG8gRnJvbnRfEA9hcnJhbmdlSW5Gcm9udDrTAA4DWANZA1oFhwWIgLuB
3148 eFRjdXQ61AAOBDIDWANZBDMAyAQ0BZaA94AYgPSBAWhYdGV4dFZpZXfUAA4EMgNYA1kEMwDIAEEFnID3
3148 AWOBAWXYAA4DXgNfA2ADYQNiA2MDZANlBYsDZwTNA2kDagNrA2yAuYEBZIEBKICzgLeAsF8QFEhpZGUg
3149 gBiAB4EBal8QFWluaXRpYWxGaXJzdFJlc3BvbmRlctMADgNYA1kDWgWgBaGAu4EBbIEBb9kADgWjA14D
3149 SVB5dGhvbjFTYW5kYm94VWhpZGU60wAOA1gDWQNaBZQFlYC7gQFngQFp2AAOA14DXwNgA2EDYgNjA2QD
3150 XwNgA2EDYgNjA2QDZgQnBaYD1wWnA2oDawNsA7ZZTlNUb29sVGlwgLmA8YEBbYEBboCzgLeAyV1QYWdl
3150 ZQWYA2cDaANpA2oDawSPgLmBAWiAsoCzgLeBARFUWm9vbVxwZXJmb3JtWm9vbTrTAA4DWANZA1oFoQWi
3151 IFNldHVwLi4uUVBecnVuUGFnZUxheW91dDrTAA4DWANZA1oFsAWxgLuBAXGBAXTYAA4DXgNfA2ADYQNi
3151 gLuBAWuBAW3ZAA4DXgNfA2ADYQNiA2MDZAO0A2UFpQNnBOkDaQNqA2sDuwCQgLmBAWyBATOAs4C3gMpf
3152 A2MDZANmBbQDaAW1A2oDawNsBJ+AuYEBcoEBc4CzgLeBARVuAFMAaABvAHcAIABTAHAAZQBsAGwAaQBu
3152 EBBTbWFydCBDb3B5L1Bhc3RlXxAYdG9nZ2xlU21hcnRJbnNlcnREZWxldGU60wAOA1gDWQNaBa4Fr4C7
3153 AGcgJlE6XxAPc2hvd0d1ZXNzUGFuZWw60wAOA1gDWQNaBb4Fv4C7gQF2gQF41wAOA14DYANhA2IDYwNk
3153 gQFvgQFy2AAOA14DXwNgA2EDYgNjA2QDZQWyA7cFswNpA2oDawOngLmBAXCBAXGAs4C3gMRoAFMAYQB2
3154 A2YFwgQnA2oDawNsA7aAuYEBd4DxgLOAt4DJXxAPUmV2ZXJ0IHRvIFNhdmVkXxAWcmV2ZXJ0RG9jdW1l
3154 AGUAIABBAHMgJlFTXxAPc2F2ZURvY3VtZW50QXM60wAOA1gDWQNaBbwFvYC7gQF0gQF32AAOA14DXwNg
3155 bnRUb1NhdmVkOtMADgNYA1kDWgXLBcyAu4EBeoEBfNgADgNeA18DYANhA2IDYwNkA2YFzwNoBCcDagNr
3155 A2EDYgNjA2QDZQXAA2cFwQNpA2oDawPNgLmBAXWBAXaAs4C3gNBTQ3V0UXhUY3V0OtMADgNYA1kDWgXK
3156 A2wEn4C5gQF7gPGAs4C3gQEVXxAbQ2hlY2sgR3JhbW1hciBXaXRoIFNwZWxsaW5nXxAWdG9nZ2xlR3Jh
3156 BcuAu4EBeYEBfNgADgNeA18DYANhA2IDYwNkA2UFzgNnBc8DaQNqA2sDp4C5gQF6gQF7gLOAt4DEVUNs
3157 bW1hckNoZWNraW5nOtcADgQyBdcF2ANYA1kF2QXaBdsF3AXdAnUF3wBVWU5TS2V5UGF0aFlOU0JpbmRp
3157 b3NlUXddcGVyZm9ybUNsb3NlOtcADgRPBdcF2ANYA1kF2QXaBFEF3AXdBd4F3wBVWU5TS2V5UGF0aFlO
3158 bmdfEBxOU05pYkJpbmRpbmdDb25uZWN0b3JWZXJzaW9ugQGLgQF+gQGKgQGCgHyBAYnbBeEADgXiBeMF
3158 U0JpbmRpbmdfEBxOU05pYkJpbmRpbmdDb25uZWN0b3JWZXJzaW9ugQGMgP2BAYuBAYqBAX6BAYnbBeEA
3159 5AXlBeYF5wXoBekF6gB6BewAegXuAHoF8AXdAHoAegB6BfVfEBpOU0ZpbHRlclJlc3RyaWN0c0luc2Vy
3159 DgXiBeMF5AXlBeYF5wXoBekF6gB6BewAegXuAHoF8AXxAHoAegB6BfVfEBpOU0ZpbHRlclJlc3RyaWN0
3160 dGlvbl8QFE5TUHJlc2VydmVzU2VsZWN0aW9uXE5TSW5pdGlhbEtleVpOU0VkaXRhYmxlXk5TRGVjbGFy
3160 c0luc2VydGlvbl8QFE5TUHJlc2VydmVzU2VsZWN0aW9uXE5TSW5pdGlhbEtleVpOU0VkaXRhYmxlXk5T
3161 ZWRLZXlzXk5TSW5pdGlhbFZhbHVlXxAiTlNDbGVhcnNGaWx0ZXJQcmVkaWNhdGVPbkluc2VydGlvbl8Q
3161 RGVjbGFyZWRLZXlzXk5TSW5pdGlhbFZhbHVlXxAiTlNDbGVhcnNGaWx0ZXJQcmVkaWNhdGVPbkluc2Vy
3162 GE5TU2VsZWN0c0luc2VydGVkT2JqZWN0c18QFk5TQXZvaWRzRW1wdHlTZWxlY3Rpb25fEBFOU1NvcnRE
3162 dGlvbl8QGE5TU2VsZWN0c0luc2VydGVkT2JqZWN0c18QFk5TQXZvaWRzRW1wdHlTZWxlY3Rpb25fEBFO
3163 ZXNjcmlwdG9ycwmBAYgJgQGBCYEBf4EBggkJCYEBg9IADgA+AGkF+IA0owX5Be4F3YEBgIEBgYEBglRr
3163 U1NvcnREZXNjcmlwdG9ycwmBAYgJgQGBCYEBf4EBggkJCYEBg9IADgA+AGkF+IA0owX5Be4F8YEBgIEB
3164 ZXlzU2tleVV2YWx1ZdIADgA+BgAGAYEBh6EGAoEBhNQADgYEBgUGBgYHBe4GCQB6VU5TS2V5Wk5TU2Vs
3164 gYEBglRrZXlzU2tleVV2YWx1ZdIADgA+BgAGAYEBh6EGAoEBhNQADgYEBgUGBgYHBe4GCQB6VU5TS2V5
3165 ZWN0b3JbTlNBc2NlbmRpbmeBAYaBAYGBAYUJWGNvbXBhcmU60gA3ADgGDQYOogYOADtfEBBOU1NvcnRE
3165 Wk5TU2VsZWN0b3JbTlNBc2NlbmRpbmeBAYaBAYGBAYUJWGNvbXBhcmU60gA3ADgGDQYOogYOADtfEBBO
3166 ZXNjcmlwdG9y0gA3ADgGEAE4ogE4ADvSADcAOAYSBhOlBhMGFAYVBhYAO18QFk5TRGljdGlvbmFyeUNv
3166 U1NvcnREZXNjcmlwdG9y0gA3ADgGEAE4ogE4ADvSADcAOAYSBhOlBhMGFAYVBhYAO18QFk5TRGljdGlv
3167 bnRyb2xsZXJfEBFOU0FycmF5Q29udHJvbGxlcl8QEk5TT2JqZWN0Q29udHJvbGxlclxOU0NvbnRyb2xs
3167 bmFyeUNvbnRyb2xsZXJfEBFOU0FycmF5Q29udHJvbGxlcl8QEk5TT2JqZWN0Q29udHJvbGxlclxOU0Nv
3168 ZXJfEBp2YWx1ZTogYXJyYW5nZWRPYmplY3RzLmtleV8QE2FycmFuZ2VkT2JqZWN0cy5rZXnSADcAOAYa
3168 bnRyb2xsZXJfEClmaWx0ZXJQcmVkaWNhdGU6IHdvcmtzcGFjZUZpbHRlclByZWRpY2F0ZV8QD2ZpbHRl
3169 BhujBhsDiwA7XxAVTlNOaWJCaW5kaW5nQ29ubmVjdG9y1wAOBDIF1wXYA1gDWQXZBdoENAYfBiAF2wYi
3169 clByZWRpY2F0ZV8QGHdvcmtzcGFjZUZpbHRlclByZWRpY2F0ZdIANwA4BhsGHKMGHAOKADtfEBVOU05p
3170 AFWBAYuA9IEBj4EBjoEBfoEBjV8QGWNvbnRlbnREaWN0aW9uYXJ5OiB1c2VyTlNfEBFjb250ZW50RGlj
3170 YkJpbmRpbmdDb25uZWN0b3LXAA4ETwXXBdgDWANZBdkF2gRvBiAGIQXeBiMAVYEBjIEBB4EBkIEBj4EB
3171 dGlvbmFyeVZ1c2VyTlPXAA4EMgXXBdgDWANZBdkF2gXbBikF3QJ2BiwAVYEBi4EBfoEBkoEBgoCLgQGR
3171 foEBjl8QGWNvbnRlbnREaWN0aW9uYXJ5OiB1c2VyTlNfEBFjb250ZW50RGljdGlvbmFyeVZ1c2VyTlPX
3172 XxAcdmFsdWU6IGFycmFuZ2VkT2JqZWN0cy52YWx1ZV8QFWFycmFuZ2VkT2JqZWN0cy52YWx1ZdcADgQy
3172 AA4ETwXXBdgDWANZBdkF2gXeBioF8QJ2Bi0AVYEBjIEBfoEBk4EBgoCLgQGSXxAcdmFsdWU6IGFycmFu
3173 BdcF2ANYA1kF2QXaBQoGMgYzBdsGNQBVgQGLgQE7gQGWgQGVgQF+gQGUXxApZmlsdGVyUHJlZGljYXRl
3173 Z2VkT2JqZWN0cy52YWx1ZV8QFWFycmFuZ2VkT2JqZWN0cy52YWx1ZdcADgRPBdcF2ANYA1kF2QXaBd4G
3174 OiB3b3Jrc3BhY2VGaWx0ZXJQcmVkaWNhdGVfEA9maWx0ZXJQcmVkaWNhdGVfEBh3b3Jrc3BhY2VGaWx0
3174 MwXxAnUGNgBVgQGMgQF+gQGWgQGCgHyBAZVfEBp2YWx1ZTogYXJyYW5nZWRPYmplY3RzLmtleV8QE2Fy
3175 ZXJQcmVkaWNhdGXXAA4EMgXXBdgDWANZBdkF2gQ0BjwGPQBsBj8AVYEBi4D0gQGagQGZgKOBAZhfEBlh
3175 cmFuZ2VkT2JqZWN0cy5rZXnXAA4ETwXXBdgDWANZBdkF2gRvBjwGPQBsBj8AVYEBjIEBB4EBmoEBmYCj
3176 bmltYXRlOiB3YWl0aW5nRm9yRW5naW5lV2FuaW1hdGVfEBB3YWl0aW5nRm9yRW5naW5l0gAOAD4GAAZF
3176 gQGYXxAZYW5pbWF0ZTogd2FpdGluZ0ZvckVuZ2luZVdhbmltYXRlXxAQd2FpdGluZ0ZvckVuZ2luZdIA
3177 gQGHrxBnA1sGRwTfAkQCdQZLBIoEQQUgBW8GUATRBbAFywZUBFYFLgZXBYYGWQIKA20GXATtBl4GXwS3
3177 DgA+BgAGRYEBh68QZwZGAGsE5AVaBkoCwAVQBk0CgwZPBlAE1gZSBlMGVARCAE0AfgPwA7sEBwT2A7EE
3178 BmEGYgBNBdsAawVVBHsFoAZpBmoAyAUKBGEGbgSpBnAAbAZyBBUD9QIaA8gAfwPqAnYEIgZ7BJcGfQOO
3178 FQP6AgoAfwNsBJoGYwOnAEECKgTIA1sEeQPfBSYFegRSBm4EbwPoAMgF3gWuBnQE7AONBUgGeAZ5BGED
3179 Bn8GgAV5AioGgwSfAhAETgPSBogEBwCqBb4D4QaNBo4D/QLAA64AfgaTBG4FEwTEAKMFXQT7BpoGmwOg
3179 nwOVA80GfgQqBoAEUQaCBREEMwIaBoYGhwaIBokAqgaLBCIFBASoA9cAbAWHBcoCRAaUA8UGlgJ2ALEF
3180 BTYDlgafBWcEKgPABDQFQQKDAEEAsQaoBqkFSAO2BqyAr4EBnIEBLIB0gHyBAaGBARCA+YEBQ4EBW4EB
3180 OgS+BpsFvAJ1Bp4FZwagBIcFoQS2AKMGpQIQBqcGqAWUBqoGqwSPgQGcgA6BATCBAVWBAZ2AjoEBUYEB
3181 poEBJ4EBcYEBeoEBqoD+gQFIgQHDgQFjgQHwgG6AsIEB2YEBMYEBv4EBz4EBH4EB5oEB5IALgQF+gA6B
3181 qICDgQGqgQGugQErgQGygQGegQG/gPeAC4AQgNuAyoDkgQE2gMmA6YDggG6AaoCwgQEWgQHYgMSAB4By
3182 AVSBAQuBAWyBAbaBAbWAGIEBO4EBA4EB6oEBGoEBxoCjgQG9gOqA34CUgM+AaoDagIuA7oEBrYEBFIEB
3182 gQEmgK+BAQuA1oEBRIEBX4D8gQHVgQEHgNqAGIEBfoEBb4EB6oEBMYC9gQFQgQHlgQHugQECgMOAvoDQ
3183 y4C9gQHQgQHugQFfgHKBAbGBARWAloD9gNSBAdGA5YBYgQF2gNmBAdeBAbyA4ICOgMiAEIEB1YEBB4EB
3183 gQG4gO6BAeGA/YEBzoEBPoDygJSBAbyBAcmBAd6BAeaAWIEBtIDtgQE6gQEbgNWAo4EBY4EBeYB0gQHN
3184 P4EBI4AUgQFVgQE2gQHcgQGygMOBAUmAvoEB4IEBWoDvgM6A9IEBToCDgAeAVIEBuYEByoEBUIDJgQHJ
3184 gM+BAdqAi4BUgQFLgQEhgQHwgQF0gHyBAbOBAVmBAcOBARCBAWuBASCAFIEB0YCWgQHSgQGigQFngQG6
3185 2gAOBq4DXgNfA2ADYQNiA2MDZAGgA2YEKgQtA2gEJwNqA2sDbAPIBrZZTlNTdWJtZW51gLmA74EBnYDx
3185 gQHkgQER2gAOA14DXwauA2AGrwNhA2IDYwNkA2UDaANnAHoDaAB6A2kDagNrA2xdTlNJc1NlcGFyYXRv
3186 gLOAt4DPgQGeVlNwZWVjaF5zdWJtZW51QWN0aW9uOtIADgA+AGkGu4A0ogVIBCKBAVCA7tIANwA4Br8D
3186 clxOU0lzRGlzYWJsZWSAuYCyCYCyCYCzgLeAsNoADga5A14DXwNgA2EDYgNjA2QBoANlBL4EwQNnA2gD
3187 ZKIDZAA72gAOBq4DXgNfA2ADYQNiA2MDZAGgA2YFbwVyA2gEJwNqA2sDbAO2BsiAuYEBW4EBooDxgLOA
3187 aQNqA2sGUwbBWU5TU3VibWVudYC5gQEhgQGfgLKAs4C3gQGegQGg1AAOA14B1QNuA28GxAbFBsaBAaSB
3188 t4DJgQGjW09wZW4gUmVjZW500gAOAD4AaQbMgDShBWeBAVpfEBZfTlNSZWNlbnREb2N1bWVudHNNZW51
3188 AceBAfSBAchWRm9ybWF0XnN1Ym1lbnVBY3Rpb2460gAOAD4AaQbLgDSiBqgEtoEBooEBINgADgNeA18D
3189 2gAOBq4DXgNfA2ADYQNiA2MDZAGgA2YDbQNxA2gEJwNqA2sDbAPIBteAuYCwgQGngPGAs4C3gM+BAahd
3189 YANhA2IDYwNkA2UG0ANnBDkDaQNqA2sEvoC5gQGjgPSAs4C3gQEhWlNob3cgRm9udHPSADcAOAbXA2Si
3190 U3Vic3RpdHV0aW9uc9IADgA+AGkG24A0owOgA1sE+4DDgK+BATbUAA4DXgHVA28DcAbhBuIG44EBoIEB
3190 A2QAO1tPcGVuIFJlY2VudNIADgA+AGkG24A0oQVIgQFQXxAWX05TUmVjZW50RG9jdW1lbnRzTWVuddoA
3191 q4EB9IEBrFlBTWFpbk1lbnXSAA4APgBpBueANKcGewZeBn0GnwZhBm4GWYEBrYEBv4EBy4EB4IEB5oEB
3191 Dga5A14DXwNgA2EDYgNjA2QBoANlBVAFUwNnA2gDaQNqA2sDpwbmgLmBAVGBAaWAsoCzgLeAxIEBqdoA
3192 6oEB8NoADgauA14DXwNgA2EDYgNjA2QBoANmA+oD7QNoBCcDagNrA2wGVAb3gLmA2oEBroDxgLOAt4EB
3192 Dga5A14DXwNgA2EDYgNjA2QBoANlA5UDmANnA2gDaQNqA2sDzQbvgLmAvoEBq4CygLOAt4DQgQGsXxAU
3193 qoEBr18QD0lQeXRob24xU2FuZGJveNIADgA+AGkG+4A0qwSKBoMGmwZqBmkGjgS3A+EEbgZyBO2BARCB
3193 U3BlbGxpbmcgYW5kIEdyYW1tYXLSAA4APgBpBvOANKQEqAONA/oEFYEBG4C9gOCA6doADga5A14DXwNg
3194 AbGBAbKBAbWBAbaBAbyBAR+A2YEBB4EBvYEBMdoADgNeA18HCANgBwkDYQNiA2MDZANmBCcDaAB6BCcA
3194 A2EDYgNjA2QBoANlA80D0ANnA2gDaQNqA2sGUwcAgLmA0IEBr4CygLOAt4EBnoEBsFRFZGl00gAOAD4A
3195 egNqA2sDbAPqXU5TSXNTZXBhcmF0b3JcTlNJc0Rpc2FibGVkgLmA8QmA8QmAs4C3gNrYAA4DXgNfA2AD
3195 aQcEgDStBJoDxQZSBbwE1gUmBVoEBwaeBosGTwZUBqCBARaAz4EBsoEBdIEBK4EBRIEBVYDkgQGzgQG0
3196 YQNiA2MDZANmBxQDaAcVA2oDawNsA+qAuYEBs4EBtICzgLeA2mwAUAByAGUAZgBlAHIAZQBuAGMAZQBz
3196 gQGqgQG/gQHD2gAOA14DXwauA2AGrwNhA2IDYwNkA2UDaANnAHoDaAB6A2kDagNrA82AuYCyCYCyCYCz
3197 ICZRLNoADgNeA18HCANgBwkDYQNiA2MDZANmBCcDaAB6BCcAegNqA2sDbAPqgLmA8QmA8QmAs4C3gNra
3197 gLeA0NoADgNeA18GrgNgBq8DYQNiA2MDZANlA2gDZwB6A2gAegNpA2oDawPNgLmAsgmAsgmAs4C3gNDa
3198 AA4GrgNeA18DYANhA2IDYwNkAaADZgaoBycDaAQnA2oDawNsA+oHLIC5gQG5gQG3gPGAs4C3gNqBAbhY
3198 AA4GuQNeA18DYANhA2IDYwNkAaADZQTsBO8DZwNoA2kDagNrA80HLIC5gQExgQG1gLKAs4C3gNCBAbZU
3199 U2VydmljZXPUAA4DXgHVA28DcAcnBzEHMoEBoIEBt4EBu4EButIADgA+AGkHNYA0oF8QD19OU1NlcnZp
3199 RmluZNIADgA+AGkHMIA0pQTkBn4GqgaGBTqBATCBAbiBAbqBAbyBAUvZAA4DXgNfA2ADYQNiA2MDZAO0
3200 Y2VzTWVuddoADgNeA18HCANgBwkDYQNiA2MDZANmBCcDaAB6BCcAegNqA2sDbAPqgLmA8QmA8QmAs4C3
3200 A2UHOANnBGYDaQNqA2sE7ABVgLmBAbmBAQSAs4C3gQExWUZpbmQgTmV4dNkADgNeA18DYANhA2IDYwNk
3201 gNraAA4DXgNfBwgDYAcJA2EDYgNjA2QDZgQnA2gAegQnAHoDagNrA2wD6oC5gPEJgPEJgLOAt4DaXF9O
3201 A7QDZQdAA7cDuANpA2oDawTsAViAuYEBu4DMgLOAt4EBMV1GaW5kIFByZXZpb3Vz2QAOA14DXwNgA2ED
3202 U0FwcGxlTWVuddoADgauA14DXwNgA2EDYgNjA2QBoANmA7YDuQNoBCcDagNrA2wGVAdSgLmAyYEBwIDx
3202 YgNjA2QDtANlB0gDZwdJA2kDagNrBOwHTYC5gQG9gQG+gLOAt4EBMRAHXxAWVXNlIFNlbGVjdGlvbiBm
3203 gLOAt4EBqoEBwVRGaWxl0gAOAD4AaQdWgDSrBlcGcAZLBqwEewOuBN8FvgapBaAE0YEBw4EBxoEBoYEB
3203 b3IgRmluZFFl2gAOBrkDXgNfA2ADYQNiA2MDZAGgA2UDuwO+A2cDaANpA2oDawPNB1iAuYDKgQHAgLKA
3204 yYEBC4DIgQEsgQF2gQHKgQFsgQEn2AAOA14DXwNgA2EDYgNjA2QDZgdkA2gHZQNqA2sDbAO2gLmBAcSB
3204 s4C3gNCBAcFdU3Vic3RpdHV0aW9uc9IADgA+AGkHXIA0owWhBGEDsYEBa4EBAoDJ2gAOBrkDXgNfA2AD
3205 AcWAs4C3gMlTTmV3UW7YAA4DXgNfA2ADYQNiA2MDZANmB20DaAduA2oDawNsA7aAuYEBx4EByICzgLeA
3205 YQNiA2MDZAGgA2UD3wPiA2cDaANpA2oDawPNB2iAuYDWgQHEgLKAs4C3gNCBAcVWU3BlZWNo0gAOAD4A
3206 yWUATwBwAGUAbiAmUW/aAA4DXgNfBwgDYAcJA2EDYgNjA2QDZgQnA2gAegQnAHoDagNrA2wDtoC5gPEJ
3206 aQdsgDSiA9cE9oDVgQE2WUFNYWluTWVuddIADgA+AGkHcoA0pwaHBpYGUAZKBokGdAabgQHJgQHagQGu
3207 gPEJgLOAt4DJ2gAOA14DXwcIA2AHCQNhA2IDYwNkA2YEJwNoAHoEJwB6A2oDawNsA7aAuYDxCYDxCYCz
3207 gQGdgQHmgQHqgQHw2gAOBrkDXgNfA2ADYQNiA2MDZAGgA2UDbANwA2cDaANpA2oDawZTB4KAuYCwgQHK
3208 gLeAydoADgauA14DXwNgA2EDYgNjA2QBoANmA8gDywNoBCcDagNrA2wGVAeOgLmAz4EBzIDxgLOAt4EB
3208 gLKAs4C3gQGegQHLXxAPSVB5dGhvbjFTYW5kYm940gAOAD4AaQeGgDSrBQQGlAaCBqUGpwZGBYcEyANb
3209 qoEBzVRFZGl00gAOAD4AaQeSgDStA8AD0gZfBYYFIAQHBEEEqQZ/BogGmgZQBkeAzoDUgQHPgQFjgQFD
3209 BmMEeYEBOoEBzYEBzoEB0YEB0oEBnIEBY4EBJoCvgQHYgQEL2gAOA14DXwauA2AGrwNhA2IDYwNkA2UD
3210 gOWA+YEBGoEB0IEB0YEB3IEBpoEBnNoADgNeA18HCANgBwkDYQNiA2MDZANmBCcDaAB6BCcAegNqA2sD
3210 aANnAHoDaAB6A2kDagNrA2yAuYCyCYCyCYCzgLeAsNgADgNeA18DYANhA2IDYwNkA2UHnQNnB54DaQNq
3211 bAPIgLmA8QmA8QmAs4C3gM/aAA4DXgNfBwgDYAcJA2EDYgNjA2QDZgQnA2gAegQnAHoDagNrA2wDyIC5
3211 A2sDbIC5gQHPgQHQgLOAt4CwbABQAHIAZQBmAGUAcgBlAG4AYwBlAHMgJlEs2gAOA14DXwauA2AGrwNh
3212 gPEJgPEJgLOAt4DP2gAOBq4DXgNfA2ADYQNiA2MDZAGgA2YDlgOZA2gEJwNqA2sDbAPIB7qAuYC+gQHS
3212 A2IDYwNkA2UDaANnAHoDaAB6A2kDagNrA2yAuYCyCYCyCYCzgLeAsNoADga5A14DXwNgA2EDYgNjA2QB
3213 gPGAs4C3gM+BAdNURmluZNIADgA+AGkHvoA0pQQVBpMGjQZcA46A6oEB1YEB14EB2YC92QAOA14DXwNg
3213 oANlBm4HsANnA2gDaQNqA2sDbAe1gLmBAdWBAdOAsoCzgLeAsIEB1FhTZXJ2aWNlc9QADgNeAdUDbgNv
3214 A2EDYgNjA2QDZQNmB8YDaANpA2oDawNsA5YAVYC5gQHWgLKAs4C3gL5ZRmluZCBOZXh02QAOA14DXwNg
3214 B7AHuge7gQGkgQHTgQHXgQHW0gAOAD4AaQe+gDSgXxAPX05TU2VydmljZXNNZW512gAOA14DXwauA2AG
3215 A2EDYgNjA2QDZQNmB84D1wUAA2oDawNsA5YBWIC5gQHYgQE4gLOAt4C+XUZpbmQgUHJldmlvdXPZAA4D
3215 rwNhA2IDYwNkA2UDaANnAHoDaAB6A2kDagNrA2yAuYCyCYCyCYCzgLeAsFxfTlNBcHBsZU1lbnXaAA4G
3216 XgNfA2ADYQNiA2MDZANlA2YH1gNoB9cDagNrA2wDlgfbgLmBAdqBAduAs4C3gL4QB18QFlVzZSBTZWxl
3216 uQNeA18DYANhA2IDYwNkAaADZQOnA6oDZwNoA2kDagNrBlMH0oC5gMSBAduAsoCzgLeBAZ6BAdxURmls
3217 Y3Rpb24gZm9yIEZpbmRRZdoADgauA14DXwNgA2EDYgNjA2QBoANmBJ8EogNoBCcDagNrA2wDyAfmgLmB
3217 ZdIADgA+AGkH1oA0qwaIBoAGTQarBcoEQgWuBWcGeAURA5+BAd6BAeGBAaiBAeSBAXmA94EBb4EBWYEB
3218 ARWBAd2A8YCzgLeAz4EB3l8QFFNwZWxsaW5nIGFuZCBHcmFtbWFy0gAOAD4AaQfqgDSkBbAElwTEBcuB
3218 5YEBPoDD2AAOA14DXwNgA2EDYgNjA2QDZQfkA2cH5QNpA2oDawOngLmBAd+BAeCAs4C3gMRTTmV3UW7Y
3219 AXGBARSBASOBAXraAA4GrgNeA18DYANhA2IDYwNkAaADZgP9BAADaAQnA2oDawNsBlQH94C5gOCBAeGA
3219 AA4DXgNfA2ADYQNiA2MDZANlB+0DZwfuA2kDagNrA6eAuYEB4oEB44CzgLeAxGUATwBwAGUAbiAmUW/a
3220 8YCzgLeBAaqBAeJWRm9ybWF00gAOAD4AaQf7gDSiBmID9YEB5IDf2AAOA14DXwNgA2EDYgNjA2QDZggA
3220 AA4DXgNfBq4DYAavA2EDYgNjA2QDZQNoA2cAegNoAHoDaQNqA2sDp4C5gLIJgLIJgLOAt4DE2gAOA14D
3221 A2gFMwNqA2sDbAP9gLmBAeWBAUuAs4C3gOBaU2hvdyBGb250c9oADgauA14DXwNgA2EDYgNjA2QBoANm
3221 XwauA2AGrwNhA2IDYwNkA2UDaANnAHoDaAB6A2kDagNrA6eAuYCyCYCyCYCzgLeAxNoADga5A14DXwNg
3222 BTYFOQNoBCcDagNrA2wGVAgOgLmBAUmBAeeA8YCzgLeBAaqBAehUVmlld9IADgA+AGkIEoA0ogUuBXmB
3222 A2EDYgNjA2QBoANlBCoELQNnA2gDaQNqA2sGUwgOgLmA7oEB54CygLOAt4EBnoEB6FRWaWV30gAOAD4A
3223 AUiBAV/aAA4GrgNeA18DYANhA2IDYwNkAaADZgRWBFkDaAQnA2oDawNsBlQIHYC5gP6BAeuA8YCzgLeB
3223 aQgSgDSiBDMEIoDygO3aAA4GuQNeA18DYANhA2IDYwNkAaADZQSPBJIDZwNoA2kDagNrBlMIHYC5gQER
3224 AaqBAexWV2luZG930gAOAD4AaQghgDSkBE4FEwaABGGA/YEBP4EB7oEBA9oADgNeA18HCANgBwkDYQNi
3224 gQHrgLKAs4C3gQGegQHsVldpbmRvd9IADgA+AGkIIYA0pASHBZQGeQV6gQEQgQFngQHugQFf2gAOA14D
3225 A2MDZANmBCcDaAB6BCcAegNqA2sDbARWgLmA8QmA8QmAs4C3gP5eX05TV2luZG93c01lbnXaAA4GrgNe
3225 XwauA2AGrwNhA2IDYwNkA2UDaANnAHoDaAB6A2kDagNrBI+AuYCyCYCyCYCzgLeBARFeX05TV2luZG93
3226 A18DYANhA2IDYwNkAaADZgVdBWADaAQnA2oDawNsBlQIOIC5gQFVgQHxgPGAs4C3gQGqgQHyVEhlbHDS
3226 c01lbnXaAA4GuQNeA18DYANhA2IDYwNkAaADZQPwA/MDZwNoA2kDagNrBlMIOIC5gNuBAfGAsoCzgLeB
3227 AA4APgBpCDyANKEFVYEBVFtfTlNNYWluTWVuddIADgA+BgAIQYEBh68QZwNtA8gDtgIKAioDtgPqA8gD
3227 AZ6BAfJUSGVscNIADgA+AGkIPIA0oQPogNpbX05TTWFpbk1lbnXSAA4APgYACEGBAYevEGcDbABNBOwD
3228 yAZLA8gDtgSfBJ8AHwZuBTYDtgPIBlQAfwZQA5YD6gZUA8gD6gZUA/0AQQAfAE0FXQO2A7YD6gPqAKMA
3228 zQZTAnYGTQOnAnUDzQZTA80DzQAfA80DpwBBAGsGmwZUA80D3wO7A5UDlQB/AGsGhwPNA2wGlgAfAgoD
3229 HwRWBlQDyAO2AE0D6gOWA/0CCgZ9AGsGewIqBCoGVASfBlQDlgPIBFYFNgIKA+oGmgIKBFYDyAPIA8gA
3229 bANsA2wGoAPNBI8AHwanAB8D8ACjAB8DpwZTBosDlQVQA6cEjwO7A6cGTwZQBOwGiQOnAB8DbAOnBCoC
3230 owO2A+oDlgPqBp8CdgO2AGsDlgPqBFYEnwB+BlkDbQPIA+oDbQZhBogGVAVvBkcDyAAfAB8CdQAfAKMG
3230 CgTsBlMDpwZTAKMDzQQqA2wDlQPfAE0DbAOnAgoDbAPNBlMCKgCjBOwGSgZTA80CKgPNA6cDzQSPA7sE
3231 aQO2BCoGXgO2gLCAz4DJgG6AcoDJgNqAz4DPgQGhgM+AyYEBFYEBFYACgQHqgQFJgMmAz4EBqoBqgQGm
3231 vgB+A2wCCgNsBL4EjwTsA6cGdICwgAuBATGA0IEBnoCLgQGogMSAfIDQgQGegNCA0IACgNCAxIAHgA6B
3232 gL6A2oEBqoDPgNqBAaqA4IAHgAKAC4EBVYDJgMmA2oDagBSAAoD+gQGqgM+AyYALgNqAvoDggG6BAcuA
3232 AfCBAb+A0IDWgMqAvoC+gGqADoEByYDQgLCBAdqAAoBugLCAsICwgQHDgNCBARGAAoEB0oACgNuAFIAC
3233 DoEBrYBygO+BAaqBARWBAaqAvoDPgP6BAUmAboDagQHcgG6A/oDPgM+Az4AUgMmA2oC+gNqBAeCAi4DJ
3233 gMSBAZ6BAbSAvoEBUYDEgQERgMqAxIEBqoEBroEBMYEB5oDEgAKAsIDEgO6AboEBMYEBnoDEgQGegBSA
3234 gA6AvoDagP6BARWAEIEB8ICwgM+A2oCwgQHmgQHRgQGqgQFbgQGcgM+AAoACgHyAAoAUgQG2gMmA74EB
3234 0IDugLCAvoDWgAuAsIDEgG6AsIDQgQGegHKAFIEBMYEBnYEBnoDQgHKA0IDEgNCBARGAyoEBIYAQgLCA
3235 v4DJ0gAOAD4GAAirgQGHrxBoA1sGRwTfAkQCdQZLBIoEQQUgBW8E0QZQBbAFywUuBlQEVgZXBYYGWQIK
3235 boCwgQEhgQERgQExgMSBAerSAA4APgYACKuBAYevEGgAawZGBOQFWgZKAsAFUAZNAoMGTwZQBNYGUgZT
3236 A20GXATtBl4GXwS3BmEGYgBNBdsAawVVBHsFoAZpBmoAyAUKBGEGbgZwBKkAbAZyBBUD9QIaA8gD6gB/
3236 BlQATQRCAH4D8AO7BAcE9gOxBBUCCgP6AH8DbABBA6cEmgZjAioEyANbBHkD3wUmBXoEUgZuBG8D6ADI
3237 BCICdgZ7BJcGfQOOBn8GgAV5AioGgwSfAhAETgPSBogEBwCqBb4D4QaNBo4D/QLAA64AfgaTBG4FEwTE
3237 Bd4AHwWuBnQGeATsA40FSAZ5BGEDnwOVA80EKgZ+BoAEUQaCBREEMwIaBoYGhwaJBogAqgaLBCIFBABs
3238 AKMFXQT7AB8GmgabBTYDoAafA5YFZwQqBDQDwAVBAoMAQQCxBqkGqAVIA7YGrICvgQGcgQEsgHSAfIEB
3238 BKgD1wWHBcoCRAaUA8UGlgJ2ALEEvgU6BpsFvAJ1Bp4FZwagBIcFoQS2AKMGpQIQBqcGqAWUBqoGqwSP
3239 oYEBEID5gQFDgQFbgQEngQGmgQFxgQF6gQFIgQGqgP6BAcOBAWOBAfCAboCwgQHZgQExgQG/gQHPgQEf
3239 gA6BAZyBATCBAVWBAZ2AjoEBUYEBqICDgQGqgQGugQErgQGygQGegQG/gAuA94AQgNuAyoDkgQE2gMmA
3240 gQHmgQHkgAuBAX6ADoEBVIEBC4EBbIEBtoEBtYAYgQE7gQEDgQHqgQHGgQEagKOBAb2A6oDfgJSAz4Da
3240 6YBugOCAaoCwgAeAxIEBFoEB2IBygQEmgK+BAQuA1oEBRIEBX4D8gQHVgQEHgNqAGIEBfoACgQFvgQHq
3241 gGqA7oCLgQGtgQEUgQHLgL2BAdCBAe6BAV+AcoEBsYEBFYCWgP2A1IEB0YDlgFiBAXaA2YEB14EBvIDg
3241 gQHlgQExgL2BAVCBAe6BAQKAw4C+gNCA7oEBuIEB4YD9gQHOgQE+gPKAlIEBvIEByYEB5oEB3oBYgQG0
3242 gI6AyIAQgQHVgQEHgQE/gQEjgBSBAVWBATaAAoEB3IEBsoEBSYDDgQHggL6BAVqA74D0gM6BAU6Ag4AH
3242 gO2BATqAo4EBG4DVgQFjgQF5gHSBAc2Az4EB2oCLgFSBASGBAUuBAfCBAXSAfIEBs4EBWYEBw4EBEIEB
3243 gFSBAcqBAbmBAVCAyYEBydIADgA+BgAJFoEBh68QaAkXCRgJGQkaCRsJHAkdCR4JHwkgCSEJIgkjCSQJ
3243 a4EBIIAUgQHRgJaBAdKBAaKBAWeBAbqBAeSBARHSAA4APgYACRaBAYevEGgJFwkYCRkJGgkbCRwJHQke
3244 JQkmCScJKAkpCSoJKwksCS0JLgkvCTAJMQkyCTMJNAk1CTYJNwk4CTkJOgk7CTwFDgk+CT8JQAlBCUIJ
3244 CR8JIAkhCSIJIwkkCSUJJgknCSgJKQkqCSsJLAktCS4JLwkwCTEJMgkzCTQJNQk2CTcJOAk5CToJOwk8
3245 QwlECUUJRglHCUgJSQlKCUsJTAlNCU4JTwlQCVEJUglTCVQJVQlWCVcJWAlZCVoJWwlcCV0JXglfCWAJ
3245 CT0JPgk/CUAJQQlCCUMJRAlFCUYJRwlICUkJSglLCUwJTQlOCU8JUAlRCVIEWQlUCVUJVglXCVgJWQla
3246 YQliCWMJZAllCWYJZwloCWkJaglrCWwJbQluCW8JcAlxCXIJcwl0CXUJdgl3CXgJeQl6CXsJfAl9CX6B
3246 CVsJXAldCV4JXwlgCWEJYgljCWQJZQlmCWcJaAlpCWoJawlsCW0JbglvCXAJcQlyCXMJdAl1CXYJdwl4
3247 AfiBAfmBAfqBAfuBAfyBAf2BAf6BAf+BAgCBAgGBAgKBAgOBAgSBAgWBAgaBAgeBAgiBAgmBAgqBAguB
3247 CXkJegl7CXwJfQl+gQH4gQH5gQH6gQH7gQH8gQH9gQH+gQH/gQIAgQIBgQICgQIDgQIEgQIFgQIGgQIH
3248 AgyBAg2BAg6BAg+BAhCBAhGBAhKBAhOBAhSBAhWBAhaBAheBAhiBAhmBAhqBAhuBAhyBAh2BATyBAh6B
3248 gQIIgQIJgQIKgQILgQIMgQINgQIOgQIPgQIQgQIRgQISgQITgQIUgQIVgQIWgQIXgQIYgQIZgQIagQIb
3249 Ah+BAiCBAiGBAiKBAiOBAiSBAiWBAiaBAieBAiiBAimBAiqBAiuBAiyBAi2BAi6BAi+BAjCBAjGBAjKB
3249 gQIcgQIdgQIegQIfgQIggQIhgQIigQIjgQIkgQIlgQImgQIngQIogQIpgQIqgQIrgQIsgQItgQIugQIv
3250 AjOBAjSBAjWBAjaBAjeBAjiBAjmBAjqBAjuBAjyBAj2BAj6BAj+BAkCBAkGBAkKBAkOBAkSBAkWBAkaB
3250 gQIwgQIxgQIygQIzgP6BAjSBAjWBAjaBAjeBAjiBAjmBAjqBAjuBAjyBAj2BAj6BAj+BAkCBAkGBAkKB
3251 AkeBAkiBAkmBAkqBAkuBAkyBAk2BAk6BAk+BAlCBAlGBAlKBAlOBAlSBAlWBAlaBAleBAliBAlmBAlqB
3251 AkOBAkSBAkWBAkaBAkeBAkiBAkmBAkqBAkuBAkyBAk2BAk6BAk+BAlCBAlGBAlKBAlOBAlSBAlWBAlaB
3252 AluBAlyBAl2BAl5fEBhNZW51IEl0ZW0gKFNtYXJ0IFF1b3RlcylfEBJNZW51IEl0ZW0gKFNwZWVjaClR
3252 AleBAliBAlmBAlqBAluBAlyBAl2BAl5aU3BsaXQgVmlld1tTZXBhcmF0b3ItM28QEQBNAGUAbgB1ACAA
3253 OF8QEVRhYmxlIEhlYWRlciBWaWV3XxAXVGFibGUgQ29sdW1uIChWYXJpYWJsZSlfEBdNZW51IEl0ZW0g
3253 SQB0AGUAbQAgACgARgBpAG4AZCAmAClfEBJNZW51IEl0ZW0gKERlbGV0ZSlfEBJNZW51IEl0ZW0gKEZv
3254 KE9wZW4gUmVjZW50KV8QIU1lbnUgSXRlbSAoQWJvdXQgSVB5dGhvbjFTYW5kYm94KV8QEk1lbnUgSXRl
3254 cm1hdClfEBtUZXh0IEZpZWxkIENlbGwgKFRleHQgQ2VsbClfEBJNZW51IChPcGVuIFJlY2VudClfEBdN
3255 bSAoRGVsZXRlKV8QEE1lbnUgSXRlbSAoQ29weSlfEBJNZW51IChPcGVuIFJlY2VudClRNl8QGU1lbnUg
3255 ZW51IEl0ZW0gKE9wZW4gUmVjZW50KV8QHVRleHQgRmllbGQgQ2VsbCAoVGV4dCBDZWxsKS0xXxAgTWVu
3256 SXRlbSAoU3Vic3RpdHV0aW9ucylvEBoATQBlAG4AdQAgAEkAdABlAG0AIAAoAFMAaABvAHcAIABTAHAA
3256 dSBJdGVtIChTcGVsbGluZyBhbmQgR3JhbW1hcilfEBBNZW51IEl0ZW0gKEVkaXQpXxAQTWVudSBJdGVt
3257 ZQBsAGwAaQBuAGcgJgApXxAnTWVudSBJdGVtIChDaGVjayBHcmFtbWFyIFdpdGggU3BlbGxpbmcpXxAY
3257 IChDb3B5KVlTZXBhcmF0b3JYTWFpbk1lbnVfEBlNZW51IEl0ZW0gKFN1YnN0aXR1dGlvbnMpXENvbnRl
3258 TWVudSBJdGVtIChTaG93IFRvb2xiYXIpWE1haW5NZW51XU1lbnUgKFdpbmRvdylROV8QD01lbnUgSXRl
3258 bnQgVmlld1EzXUJveCAoQ29uc29sZSlRMl8QFE1lbnUgKFN1YnN0aXR1dGlvbnMpXxAWTWVudSBJdGVt
3259 bSAoQ3V0KVExW1Njcm9sbCBWaWV3XxAUTWVudSAoU3Vic3RpdHV0aW9ucylfECJNZW51IEl0ZW0gKFVz
3259 IChTZWxlY3QgQWxsKV8QGU1lbnUgSXRlbSAoU3RvcCBTcGVha2luZylfEBdNZW51IEl0ZW0gKFNtYXJ0
3260 ZSBTZWxlY3Rpb24gZm9yIEZpbmQpVDExMTFfEBBNZW51IEl0ZW0gKEZpbGUpW1NlcGFyYXRvci01XxAg
3260 IExpbmtzKV8QJ01lbnUgSXRlbSAoQ2hlY2sgR3JhbW1hciBXaXRoIFNwZWxsaW5nKV1TY3JvbGwgVmll
3261 TWVudSBJdGVtIChIaWRlIElQeXRob24xU2FuZGJveClfEBBNZW51IEl0ZW0gKFZpZXcpXxAWTWVudSBJ
3261 dy0xXxAnTWVudSBJdGVtIChDaGVjayBTcGVsbGluZyBXaGlsZSBUeXBpbmcpXxAPQm94IChXb3Jrc3Bh
3262 dGVtIChTaG93IEZvbnRzKVxDb250ZW50IFZpZXdfEBlVc2VyIE5hbWVzcGFjZSBDb250cm9sbGVyWlNw
3262 Y2UpXxAWTWVudSAoSVB5dGhvbjFTYW5kYm94KV8QGVdpbmRvdyAoSVB5dGhvbjEgKENvY29hKSlbTWVu
3263 bGl0IFZpZXdfECBNZW51IEl0ZW0gKElQeXRob24xU2FuZGJveCBIZWxwKVMxLTFRNV8QFE1lbnUgSXRl
3263 dSAoRmlsZSlfEBBNZW51IEl0ZW0gKFVuZG8pW1NlcGFyYXRvci00XxAcVGFibGUgVmlldyAoVmFyaWFi
3264 bSAoU2VydmljZXMpW1NlcGFyYXRvci0xWVRleHQgVmlld18QHk1lbnUgSXRlbSAoQnJpbmcgQWxsIHRv
3264 bGUsIFZhbHVlKV8QF01lbnUgSXRlbSAoSGlkZSBPdGhlcnMpXxAUTWVudSBJdGVtIChTaG93IEFsbClU
3265 IEZyb250KV8QEk1lbnUgSXRlbSAoV2luZG93KW8QEQBNAGUAbgB1ACAASQB0AGUAbQAgACgATwBwAGUA
3265 MTExMV1NZW51IChTcGVlY2gpXxARTWVudSBJdGVtIChQYXN0ZSlfEB5NZW51IEl0ZW0gKEJyaW5nIEFs
3266 biAmAClfEBZNZW51IEl0ZW0gKFNlbGVjdCBBbGwpXEFzeW5jIEFycm93c1tTZXBhcmF0b3ItMm8QEQBN
3266 bCB0byBGcm9udClbQXBwbGljYXRpb25fEA9NZW51IChTZXJ2aWNlcylfEBdQeXRob24gQ29jb2EgQ29u
3267 AGUAbgB1ACAASQB0AGUAbQAgACgARgBpAG4AZCAmAClfEBdNZW51IEl0ZW0gKFNob3cgQ29sb3JzKV8Q
3267 dHJvbGxlcl8QIE1lbnUgSXRlbSAoSVB5dGhvbjFTYW5kYm94IEhlbHApWVRleHQgVmlld18QGVVzZXIg
3268 EVZlcnRpY2FsIFNjcm9sbGVyW01lbnUgKEVkaXQpXxAWTWVudSAoSVB5dGhvbjFTYW5kYm94KV8QD0Jv
3268 TmFtZXNwYWNlIENvbnRyb2xsZXJcRmlsZSdzIE93bmVyUThfEBJNZW51IEl0ZW0gKFdpbmRvdylTMi0x
3269 eCAoV29ya3NwYWNlKV8QGU1lbnUgSXRlbSAoU3RvcCBTcGVha2luZylfEBRUYWJsZSBDb2x1bW4gKFZh
3269 W01lbnUgKEZpbmQpXxAaTWVudSBJdGVtIChDaGVjayBTcGVsbGluZylfEBZNZW51IEl0ZW0gKENsZWFy
3270 bHVlKV8QG01lbnUgSXRlbSAoSVB5dGhvbjFTYW5kYm94KV8QGk1lbnUgSXRlbSAoQ2hlY2sgU3BlbGxp
3270 IE1lbnUpW1NlcGFyYXRvci0yXxAYTWVudSBJdGVtIChTbWFydCBRdW90ZXMpUTZfEBtNZW51IChTcGVs
3271 bmcpXxAQTWVudSBJdGVtIChFZGl0KV8QHU1lbnUgSXRlbSAoSnVtcCB0byBTZWxlY3Rpb24pW1NlcGFy
3271 bGluZyBhbmQgR3JhbW1hcilbTWVudSAoRWRpdClbTWVudSAoVmlldylfEBVNZW51IEl0ZW0gKEZpbmQg
3272 YXRvci02WVNlcGFyYXRvcm8QHgBNAGUAbgB1ACAASQB0AGUAbQAgACgAQwB1AHMAdABvAG0AaQB6AGUA
3272 TmV4dClvEBEATQBlAG4AdQAgAEkAdABlAG0AIAAoAE8AcABlAG4gJgApUzEyMVE1XxAYTWVudSBJdGVt
3273 IABUAG8AbwBsAGIAYQByICYAKV8QHFRhYmxlIFZpZXcgKFZhcmlhYmxlLCBWYWx1ZSlbU2VwYXJhdG9y
3273 IChTaG93IFRvb2xiYXIpXxATVmVydGljYWwgU2Nyb2xsZXItMV8QIk1lbnUgSXRlbSAoVXNlIFNlbGVj
3274 LTNfEBtNZW51IChTcGVsbGluZyBhbmQgR3JhbW1hcilfEBNIb3Jpem9udGFsIFNjcm9sbGVyXxAUTWVu
3274 dGlvbiBmb3IgRmluZClfEBtNZW51IEl0ZW0gKElQeXRob24xU2FuZGJveClfEBBNZW51IEl0ZW0gKFZp
3275 dSBJdGVtIChNaW5pbWl6ZSlfEBBNZW51IEl0ZW0gKFJlZG8pXxAQTWVudSBJdGVtIChGaW5kKV8QEU1l
3275 ZXcpUTlfEBNIb3Jpem9udGFsIFNjcm9sbGVyXxAQTWVudSBJdGVtIChGaW5kKW8QHgBNAGUAbgB1ACAA
3276 bnUgSXRlbSAoUGFzdGUpXxAVSG9yaXpvbnRhbCBTY3JvbGxlci0xUjEwXxAXTWVudSBJdGVtIChIaWRl
3276 SQB0AGUAbQAgACgAQwB1AHMAdABvAG0AaQB6AGUAIABUAG8AbwBsAGIAYQByICYAKV8QIU1lbnUgSXRl
3277 IE90aGVycylfEBlNZW51IEl0ZW0gKEZpbmQgUHJldmlvdXMpW1NlcGFyYXRvci00XU1lbnUgKEZvcm1h
3277 bSAoQWJvdXQgSVB5dGhvbjFTYW5kYm94KVxBc3luYyBBcnJvd3NvEBoATQBlAG4AdQAgAEkAdABlAG0A
3278 dClfEB1UZXh0IEZpZWxkIENlbGwgKFRleHQgQ2VsbCktMVEzXUJveCAoQ29uc29sZSlfEBVNZW51IEl0
3278 IAAoAFMAaABvAHcAIABTAHAAZQBsAGwAaQBuAGcgJgApXxAaTWVudSBJdGVtIChTdGFydCBTcGVha2lu
3279 ZW0gKEZpbmQgTmV4dClfEBRNZW51IEl0ZW0gKFNob3cgQWxsKV8QEE1lbnUgSXRlbSAoWm9vbSlfECdN
3279 ZylfECBNZW51IEl0ZW0gKEhpZGUgSVB5dGhvbjFTYW5kYm94KVMxLTFfEBFUYWJsZSBIZWFkZXIgVmll
3280 ZW51IEl0ZW0gKENoZWNrIFNwZWxsaW5nIFdoaWxlIFR5cGluZyldU2Nyb2xsIFZpZXctMVEyXxAXTWVu
3280 d1tTZXBhcmF0b3ItNV8QEE1lbnUgSXRlbSAoUmVkbylfEBBNZW51IEl0ZW0gKEZpbGUpXxAUVGFibGUg
3281 dSBJdGVtIChTbWFydCBMaW5rcylcRmlsZSdzIE93bmVyXxAgTWVudSBJdGVtIChTcGVsbGluZyBhbmQg
3281 Q29sdW1uIChWYWx1ZSlfEBFWZXJ0aWNhbCBTY3JvbGxlcl1NZW51IChGb3JtYXQpXxAdTWVudSBJdGVt
3282 R3JhbW1hcilTMTIxW01lbnUgKFZpZXcpXxAcTWVudSBJdGVtIChTbWFydCBDb3B5L1Bhc3RlKV8QEk1l
3282 IChKdW1wIHRvIFNlbGVjdGlvbilRMV8QD01lbnUgSXRlbSAoQ3V0KV8QF1RhYmxlIENvbHVtbiAoVmFy
3283 bnUgSXRlbSAoRm9ybWF0KVtNZW51IChGaW5kKV8QFk1lbnUgSXRlbSAoQ2xlYXIgTWVudSldTWVudSAo
3283 aWFibGUpW1NlcGFyYXRvci0xUjEwXxASTWVudSBJdGVtIChTcGVlY2gpXxAUTWVudSBJdGVtIChNaW5p
3284 U3BlZWNoKV8QF1B5dGhvbiBDb2NvYSBDb250cm9sbGVyXxAQTWVudSBJdGVtIChVbmRvKVtBcHBsaWNh
3284 bWl6ZSlfEBxNZW51IEl0ZW0gKFNtYXJ0IENvcHkvUGFzdGUpXxAXTWVudSBJdGVtIChTaG93IENvbG9y
3285 dGlvbl8QG1RleHQgRmllbGQgQ2VsbCAoVGV4dCBDZWxsKV8QGVdpbmRvdyAoSVB5dGhvbjEgKENvY29h
3285 cylbU2Nyb2xsIFZpZXdbU2VwYXJhdG9yLTZfEBVIb3Jpem9udGFsIFNjcm9sbGVyLTFfEBRNZW51IEl0
3286 KSlfEBNWZXJ0aWNhbCBTY3JvbGxlci0xUzItMV8QD01lbnUgKFNlcnZpY2VzKV8QGk1lbnUgSXRlbSAo
3286 ZW0gKFNlcnZpY2VzKV8QFk1lbnUgSXRlbSAoU2hvdyBGb250cylfEBBNZW51IEl0ZW0gKFpvb20pXxAZ
3287 U3RhcnQgU3BlYWtpbmcpW01lbnUgKEZpbGUpUTfSAA4APgYACeiBAYeg0gAOAD4GAAnrgQGHoNIADgA+
3287 TWVudSBJdGVtIChGaW5kIFByZXZpb3VzKVE3XU1lbnUgKFdpbmRvdynSAA4APgYACeiBAYeg0gAOAD4G
3288 BgAJ7oEBh68QlwNUA1sGRwNBA0kE3wJEAnUGSwSKBEEFIAVvA0IGUATRA0QDMgNOBbADMQNRA0sFywM+
3288 AAnrgQGHoNIADgA+BgAJ7oEBh68QlwZGAGsDUATkBVoGSgM8Az8CwAVQBk0DLQNHA1UCgwNEBk8GUAMr
3289 AzoGVARWBS4GVwNNBYYGWQMwAgoDbQZcBO0GXgZfAzkDOAS3AywDTAZhBmIDLwBNBdsDQAM0AGsFVQR7
3289 A08E1gZSAygDSAZTBlQEQgBNAH4D8AO7BAcE9gOxA0kEFQP6AgoAfwNsBJoGYwOnAEECKgTIA0EDWwNG
3290 BaAGaQZqAMgDKwUKBGEGbgSpBnAAbANHBnIEFQP1AhoDyAB/A+oCdgQiAz8GewSXBn0DjgZ/A0oDVgaA
3290 BHkD3wNKBSYFegRSAzQDTgNUA1EDMgM3A00GbgRvA+gAyAXeAB8FrgZ0BOwDjQVIBngDMQM6BnkEYQOf
3291 A1UFeQIqA0MGgwSfAhADKgROA9IGiAQHAKoDOwNIBb4D4QaNAzYDPAaOA0UD/QMoAsADrgMzAH4GkwRu
3291 AzsDlQPNAzkGfgQqAykGgARRBoIFEQQzAhoDOANFAyoGhgMwBocGiAaJAKoGiwQiAywFBASoA9cAbAWH
3292 BRMExACjA1IDKQVdBPsAHwaaBpsDoAU2A5YGnwMuBWcDUAQqA8AENANGBUEDNQKDA08AQQCxBqgGqQM3
3292 A0wFygM2AkQGlAPFBpYCdgNCAzMDLgCxBToEvgabBbwCdQaeBWcGoAM1Az0EhwNLBaEEtgM+AKMDUgal
3293 Az0DUwMtBUgDtgasgQGQgK+BAZyBATWBAVmBASyAdIB8gQGhgQEQgPmBAUOBAVuBATqBAaaBASeBAUKA
3293 AhADVganBqgDQANDBZQGqgMvA1MGqwSPgQGcgA6BAXOBATCBAVWBAZ2BAR+BAS+AjoEBUYEBqIDUgQFP
3294 7YEBa4EBcYDpgQF5gQFigQF6gQEmgQETgQGqgP6BAUiBAcOBAWmBAWOBAfCA5IBugLCBAdmBATGBAb+B
3294 gQGUgIOBAUOBAaqBAa6AyIEBboEBK4EBsoCugQFUgQGegQG/gPeAC4AQgNuAyoDkgQE2gMmBAViA6YDg
3295 Ac+BAQ+BAQqBAR+AzYEBZ4EB5oEB5IDegAuBAX6BATCA+IAOgQFUgQELgQFsgQG2gQG1gBiAx4EBO4EB
3295 gG6AaoCwgQEWgQHYgMSAB4BygQEmgQE5gK+BAUqBAQuA1oEBXIEBRIEBX4D8gPaBAWqBAZGBAXiA7IEB
3296 A4EB6oEBGoEBxoCjgQFPgQG9gOqA34CUgM+AaoDagIuA7oEBK4EBrYEBFIEBy4C9gQHQgQFegQGXgQHu
3296 BoEBZoEB1YEBB4DagBiBAX6AAoEBb4EB6oEBMYC9gQFQgQHlgOiBARWBAe6BAQKAw4EBGoC+gNCBAQ+B
3297 gQGTgQFfgHKBAT6BAbGBARWAloDCgP2A1IEB0YDlgFiBARmBAVOBAXaA2YEB14EBAoEBHoEBvIEBR4Dg
3297 AbiA7oC8gQHhgP2BAc6BAT6A8oCUgQEKgQFIgMKBAbyA44EByYEB3oEB5oBYgQG0gO2AzoEBOoEBG4DV
3298 gK6AjoDIgPOAEIEB1YEBB4EBP4EBI4AUgQF9gLyBAVWBATaAAoEB3IEBsoDDgQFJgL6BAeCA2IEBWoEB
3298 gKOBAWOBAWKBAXmBAQGAdIEBzYDPgQHagIuBAT2A8YDZgFSBAUuBASGBAfCBAXSAfIEBs4EBWYEBw4D7
3299 dYDvgM6A9IEBTYEBToD8gIOBAXCAB4BUgQG5gQHKgQEGgQEigQGMgNOBAVCAyYEBydIADgA+BgAKiIEB
3299 gQElgQEQgQFegQFrgQEggQEqgBSBAX2BAdGAloEBl4EB0oEBooEBNYEBQoEBZ4EBuoDfgQGNgQHkgQER
3300 h68QlwqJCooKiwqMCo0KjgqPCpAKkQqSCpMKlAqVCpYKlwqYCpkKmgqbCpwKnQqeCp8KoAqhCqIKowqk
3300 0gAOAD4GAAqIgQGHrxCXCokKigqLCowKjQqOCo8KkAqRCpIKkwqUCpUKlgqXCpgKmQqaCpsKnAqdCp4K
3301 CqUKpgqnCqgKqQqqCqsKrAqtCq4KrwqwCrEKsgqzCrQKtQq2CrcKuAq5CroKuwq8Cr0Kvgq/CsAKwQrC
3301 nwqgCqEKogqjCqQKpQqmCqcKqAqpCqoKqwqsCq0KrgqvCrAKsQqyCrMKtAq1CrYKtwq4CrkKugq7CrwK
3302 CsMKxArFCsYKxwrICskKygrLCswKzQrOCs8K0ArRCtIK0wrUCtUK1grXCtgK2QraCtsK3ArdCt4K3wrg
3302 vQq+Cr8KwArBCsIKwwrECsUKxgrHCsgKyQrKCssKzArNCs4KzwrQCtEK0grTCtQK1QrWCtcK2ArZCtoK
3303 CuEK4grjCuQK5QrmCucK6ArpCuoK6wrsCu0K7grvCvAK8QryCvMK9Ar1CvYK9wr4CvkK+gr7CvwK/Qr+
3303 2wrcCt0K3grfCuAK4QriCuMK5ArlCuYK5wroCukK6grrCuwK7QruCu8K8ArxCvIK8wr0CvUK9gr3CvgK
3304 Cv8LAAsBCwILAwsECwULBgsHCwgLCQsKCwsLDAsNCw4LDwsQCxELEgsTCxQLFQsWCxcLGAsZCxoLGwsc
3304 +Qr6CvsK/Ar9Cv4K/wsACwELAgsDCwQLBQsGCwcLCAsJCwoLCwsMCw0LDgsPCxALEQsSCxMLFAsVCxYL
3305 Cx0LHgsfgQJjgQJkgQJlgQJmgQJngQJogQJpgQJqgQJrgQJsgQJtgQJugQJvgQJwgQJxgQJygQJzgQJ0
3305 FwsYCxkLGgsbCxwLHQseCx+BAmOBAmSBAmWBAmaBAmeBAmiBAmmBAmqBAmuBAmyBAm2BAm6BAm+BAnCB
3306 gQJ1gQJ2gQJ3gQJ4gQJ5gQJ6gQJ7gQJ8gQJ9gQJ+gQJ/gQKAgQKBgQKCgQKDgQKEgQKFgQKGgQKHgQKI
3306 AnGBAnKBAnOBAnSBAnWBAnaBAneBAniBAnmBAnqBAnuBAnyBAn2BAn6BAn+BAoCBAoGBAoKBAoOBAoSB
3307 gQKJgQKKgQKLgQKMgQKNgQKOgQKPgQKQgQKRgQKSgQKTgQKUgQKVgQKWgQKXgQKYgQKZgQKagQKbgQKc
3307 AoWBAoaBAoeBAoiBAomBAoqBAouBAoyBAo2BAo6BAo+BApCBApGBApKBApOBApSBApWBApaBApeBApiB
3308 gQKdgQKegQKfgQKggQKhgQKigQKjgQKkgQKlgQKmgQKngQKogQKpgQKqgQKrgQKsgQKtgQKugQKvgQKw
3308 ApmBApqBApuBApyBAp2BAp6BAp+BAqCBAqGBAqKBAqOBAqSBAqWBAqaBAqeBAqiBAqmBAqqBAquBAqyB
3309 gQKxgQKygQKzgQK0gQK1gQK2gQK3gQK4gQK5gQK6gQK7gQK8gQK9gQK+gQK/gQLAgQLBgQLCgQLDgQLE
3309 Aq2BAq6BAq+BArCBArGBArKBArOBArSBArWBAraBAreBAriBArmBArqBAruBAryBAr2BAr6BAr+BAsCB
3310 gQLFgQLGgQLHgQLIgQLJgQLKgQLLgQLMgQLNgQLOgQLPgQLQgQLRgQLSgQLTgQLUgQLVgQLWgQLXgQLY
3310 AsGBAsKBAsOBAsSBAsWBAsaBAseBAsiBAsmBAsqBAsuBAsyBAs2BAs6BAs+BAtCBAtGBAtKBAtOBAtSB
3311 gQLZgQLagQLbgQLcgQLdgQLegQLfgQLggQLhgQLigQLjgQLkgQLlgQLmgQLngQLogQLpgQLqgQLrgQLs
3311 AtWBAtaBAteBAtiBAtmBAtqBAtuBAtyBAt2BAt6BAt+BAuCBAuGBAuKBAuOBAuSBAuWBAuaBAueBAuiB
3312 gQLtgQLugQLvgQLwgQLxgQLygQLzgQL0gQL1gQL2gQL3gQL4gQL5EQGrEQFfENMRAWUQfxBQEQGYEQGc
3312 AumBAuqBAuuBAuyBAu2BAu6BAu+BAvCBAvGBAvKBAvOBAvSBAvWBAvaBAveBAviBAvkQkBEBpRDkENEQ
3313 EHwQOhDKEMUQfREBuREBXBBOEOAQ4xBXEMwQ8REBWxDkEQFaEFYQ4RAdEBgRASkQUhEBvRDHEGcQ4hEB
3313 yhEBKxEBaRDxEQGeEH0QfBDpEH8RAawRAZ8Q4hDYENkRAWURAWsQxRDOEQFyEOsQHREBXBBLEQF0EQGk
3314 lxEBXRDdEIgQUxDOEI4QwRCGEN8RAbwRAScRAVgRAWkRAXQRAYERAXEQ6xEBpRBvEEkQTRCDEI8RAaMR
3314 EGoRAV0QxhDDEQFiEQFsEQFaENsRAZcRAZYQORDPEJUQUREBcxEBmxCREI4QlhD1EIgQ1BEBvBDLEAUT
3315 AWoRAXUQBRATEMYQSBEBtBDpEJUQ0REBWREBmRDNEQGWEDkRAZ0QwxEBaxA4EMkQ2RDSENYRAW0RAbUQ
3315 //////////0RAWoRAWMRAasQwREBbREBuRDwEIIRAaYQbxEBoxEBgREBvhBQEBMQ3BDJEH4QShEBWxDf
3316 XBEBuBEBKhEBmxDwEOwQyBEBmhEBYxAXENcQ2hDLEQGiEOgRAWgQcBCRENUQJxEBbxCQEQFuEQEsEQFk
3316 EFwRAV8QThDmEMgQzRAlENARASgQ4RBIEQF1EIEQTREBKREBmREBcREBvRBWEN0Q6BA4EFIRAScRAaIQ
3317 EQGeEEsRAa0RAaQQ0BCWEO8Q2xEBoBEBrBD1EGoRAWIRAb4Q2BCBEQFeEQEoENwRASsRAXAQfhEBbBDU
3317 2hEBKhDnEDoQzBDEEQG0EIYRAW8QSREBZBEBmBDsENcQUxEBnRBXEQFuEQFoEQGhENIRASwQZxDHEQGc
3318 EM8RAaYRAXYT//////////0QJREBnxDmEQFzEQGhEIIQShEBchDeEQGoEOcQxBBREE/SAA4APgBpC7mA
3318 ENYQcBDTEQF2EQFwEBcQJxEBXhEBWRDgEQGgEQG4EI8RAZoRAbUQgxEBWBDjEQGtEO8Q1RDeEQGoEE8Q
3319 NKDSAA4APgYAC7yBAYeg0gAOAD4GAAu/gQGHoNIANwA4C8ELwqILwgA7Xk5TSUJPYmplY3REYXRhAAgA
3319 GNIADgA+AGkLuYA0oNIADgA+BgALvIEBh6DSAA4APgYAC7+BAYeg0gA3ADgLwQvCogvCADteTlNJQk9i
3320 GQAiACcAMQA6AD8ARABSAFQAZgZmBmwGtwa+BsUG0wblBwEHDwcbBycHNQdAB04Hagd4B4sHnQe3B8EH
3320 amVjdERhdGEACAAZACIAJwAxADoAPwBEAFIAVABmBmYGbAa3Br4GxQbTBuUHAQcPBxsHJwc1B0AHTgdq
3321 zgfQB9MH1gfZB9wH3gfhB+MH5gfpB+wH7wfxB/MH9gf5B/wH/wgICBQIFggYCCYILwg4CEMISAhXCGAI
3321 B3gHiwedB7cHwQfOB9AH0wfWB9kH3AfeB+EH4wfmB+kH7AfvB/EH8wf2B/kH/Af/CAgIFAgWCBgIJggv
3322 cwh8CIcIiQiMCI4IuwjICNUI6wj5CQMJEQkeCTAJRAlQCVIJVAlWCVgJWglfCWEJYwllCWcJaQmECZcJ
3322 CDgIQwhICFcIYAhzCHwIhwiJCIwIjgi7CMgI1QjrCPkJAwkRCR4JMAlECVAJUglUCVYJWAlaCV8JYQlj
3323 oAm9Cc8J2gnjCe8J+wn9Cf8KAQoECgYKCAoKChMKFQoaChwKHgpHCk8KXgptCnoKfAp+CoAKggqFCocK
3323 CWUJZwlpCYQJlwmgCb0JzwnaCeMJ7wn7Cf0J/woBCgQKBgoICgoKEwoVChoKHAoeCkcKTwpeCm0Kegp8
3324 iQqLCowKlQqXCpwKngqgCtkK4wrvCv0LCgsUCyYLNAs2CzgLOgs8Cz0LPwtBC0MLRQtHC0kLSwtNC1YL
3324 Cn4KgAqCCoUKhwqJCosKjAqVCpcKnAqeCqAK2QrjCu8K/QsKCxQLJgs0CzYLOAs6CzwLPQs/C0ELQwtF
3325 WAtbC10Legt8C34LgAuCC4QLhguPC5ELlAuWC8cL0wvcC+gL9gv4C/oL/Av+DAEMAwwFDAcMCQwLDA0M
3325 C0cLSQtLC00LVgtYC1sLXQt6C3wLfguAC4ILhAuGC48LkQuUC5YLxwvTC9wL6Av2C/gL+gv8C/4MAQwD
3326 FgwYDB8MIQwjDCUMWgxjDGwMdgyADIoMjAyODJAMkgyUDJYMmAybDJ0MnwyhDKMMpQyuDLAMswy1DOoM
3326 DAUMBwwJDAsMDQwWDBgMHwwhDCMMJQxaDGMMbAx2DIAMigyMDI4MkAySDJQMlgyYDJsMnQyfDKEMowyl
3327 /A0GDRMNHw0pDTINPQ0/DUENQw1FDUcNSQ1LDU4NUA1SDVQNVg1YDWENYw2IDYoNjA2ODZANkg2UDZYN
3327 DK4MsAyzDLUM6gz8DQYNEw0fDSkNMg09DT8NQQ1DDUUNRw1JDUsNTg1QDVINVA1WDVgNYQ1jDYgNig2M
3328 mA2aDZwNng2gDaINpA2mDagNqg3GDdsN+A4ZDjUOWw6BDp8Ouw7XDvQPDA8mD1oPdw+TD8APyQ/QD90P
3328 DY4NkA2SDZQNlg2YDZoNnA2eDaANog2kDaYNqA2qDcYN2w34DhkONQ5bDoEOnw67DtcO9A8MDyYPWg93
3329 4w/6EA8QGRAkECwQPhBAEEIQSxBNEGIQdRCDEI0QjxCREJMQlRCiEKsQrRCvELEQuhDEEMYQxxDQENcQ
3329 D5MPwA/JD9AP3Q/jD/oQDxAZECQQLBA+EEAQQhBLEE0QYhB1EIMQjRCPEJEQkxCVEKIQqxCtEK8QsRC6
3330 6RDyEPsRFxEsETURNxE6ETwRRRFMEVsRYxFsEXERehF/EaARqBHCEdUR6RIAEhUSKBIqEi8SMRIzEjUS
3330 EMQQxhDHENAQ1xDpEPIQ+xEXESwRNRE3EToRPBFFEUwRWxFjEWwRcRF6EX8RoBGoEcIR1RHpEgASFRIo
3331 NxI5EjsSSBJVElsSXRJ4EoEShhKOEpsSoxKlEqcSqhK3Er8SwRLGEsgSyhLPEtES0xLoEvQTAhMEEwYT
3331 EioSLxIxEjMSNRI3EjkSOxJIElUSWxJdEngSgRKGEo4SmxKjEqUSpxKqErcSvxLBEsYSyBLKEs8S0RLT
3332 CBMKExETLxM8Ez4TShNfE2ETYxNlE2cTexOEE4kTlhOjE6UTqhOsE64TsxO1E7cTwxPQE9IT2RPiE+cT
3332 EugS9BMCEwQTBhMIEwoTERMvEzwTPhNKE18TYRNjE2UTZxN7E4QTiROWE6MTpROqE6wTrhOzE7UTtxPD
3333 /hQLFBMUHBQnFC4UNRRBFFgUcBR9FH8UghSPFJkUphSoFKoUshS7FMAUyRTSFN0VAhULFRQVHhUgFSIV
3333 E9AT0hPZE+IT5xP+FAsUExQcFCcULhQ1FEEUWBRwFH0UfxSCFI8UmRSmFKgUqhSyFLsUwBTJFNIU3RUC
3334 JBUmFS8VMRUzFTUVPhVWFWMVbBV3FYIVjBW5FcQVxhXIFcoVzBXOFdAV0hXbFeQV/xYYFiEWKhY3Fk4W
3334 FQsVFBUeFSAVIhUkFSYVLxUxFTMVNRU+FVYVYxVsFXcVghWMFbkVxBXGFcgVyhXMFc4V0BXSFdsV5BX/
3335 VxZeFmkWcBaNFpkWpBauFrsWxxbMFs4W0BbSFtQW1hbeFu8W9hb9FwYXCBcRFxMXFhcjFywXMRc4F00X
3335 FhgWIRYqFjcWThZXFl4WaRZwFo0WmRakFq4WuxbHFswWzhbQFtIW1BbWFt4W7xb2Fv0XBhcIFxEXExcW
3336 TxdRF1MXVRdrF3gXeheIF5EXmhesF7kXwBfJF9IX2BgRGBMYFRgXGBkYGhgcGB4YIBgiGCQYJhgvGDEY
3336 FyMXLBcxFzgXTRdPF1EXUxdVF2sXeBd6F4gXkReaF6wXuRfAF8kX0hfYGBEYExgVGBcYGRgaGBwYHhgg
3337 NBg2GFMYVRhXGFkYWxhdGF8YaBhqGG0YbxiuGLsYzhjbGN0Y3xjhGOMY5RjnGOkY6xj+GQAZAhkEGQYZ
3337 GCIYJBgmGC8YMRg0GDYYUxhVGFcYWRhbGF0YXxhoGGoYbRhvGK4YuxjOGNsY3RjfGOEY4xjlGOcY6Rjr
3338 CBkRGRMZHhkgGSIZJBkmGSgZVRlXGVkZWxldGV8ZYRljGWUZZxlwGXIZdRl3Gc4Z8Bn6GgcaHBo2GlIa
3338 GP4ZABkCGQQZBhkIGREZExkeGSAZIhkkGSYZKBlVGVcZWRlbGV0ZXxlhGWMZZRlnGXAZchl1GXcZzhnw
3339 bRp3GoMalRqkGsMazxrRGtMa3BreGuAa4RrjGuwa9Rr3Gvga+hr8Gv4bABsJGxQbMRs9Gz8bQRtDG0Ub
3339 GfoaBxocGjYaUhptGncagxqVGqQawxrPGtEa0xrcGt4a4BrhGuMa7Br1Gvca+Br6Gvwa/hsAGwkbFBsx
3340 RxtJG3YbeBt6G3wbfhuAG4IbhBuGG4gbkhubG6QbuBvRG9Mb1RvXG9kb2xvyG/scBBwSHBscHRwiHCQc
3340 Gz0bPxtBG0MbRRtHG0kbdht4G3obfBt+G4AbghuEG4YbiBuSG5sbpBu4G9Eb0xvVG9cb2RvbG/Ib+xwE
3341 JhxPHF4caxx2HIUckBybHKgcqRyrHK0cthy4HMEcyhzLHM0c6hzvHPEc8xz1HPcc+R0CHQ8dER0dHTId
3341 HBIcGxwdHCIcJBwmHE8cXhxrHHYchRyQHJscqBypHKscrRy2HLgcwRzKHMsczRzqHO8c8RzzHPUc9xz5
3342 NB02HTgdOh1MHVUdYB10HZUdox2oHaodrB2uHbAdsh21HbcdwR3SHdQd3R3fHeId9x35Hfsd/R3/Hhge
3342 HQIdDx0RHR0dMh00HTYdOB06HUwdVR1gHXQdlR2jHagdqh2sHa4dsB2yHbUdtx3BHdId1B3dHd8d4h33
3343 LR4vHjEeMx41HkgeUR5WHmQejR6OHpAekh6bHp0enh6gHr0evx7BHsMexR7HHs0e7h7wHvIe9B72Hvge
3343 Hfkd+x39Hf8eGB4tHi8eMR4zHjUeSB5RHlYeZB6NHo4ekB6SHpsenR6eHqAevR6/HsEewx7FHscezR7u
3344 +h8PHxEfEx8VHxcfIR8uHzAfNR8+H0kfYR+GH4gfih+MH44fkB+SH5QfnR+2H98f4R/jH+Uf5x/pH+sf
3344 HvAe8h70HvYe+B76Hw8fER8THxUfFx8hHy4fMB81Hz4fSR9hH4YfiB+KH4wfjh+QH5IflB+dH7Yf3x/h
3345 7R/2IA4gFyAZIBwgHiA0IE0gZCB9IJognCCeIKAgoiCkIK4guyC9INYg+SECIQshFyFAIUshViFgIW0h
3345 H+Mf5R/nH+kf6x/tH/YgDiAXIBkgHCAeIDQgTSBkIH0gmiCcIJ4goCCiIKQgriC7IL0g1iD5IQIhCyEX
3346 byFxIXMhfCGFIYghiiGNIY8hkSGWIZghoSGmIbEhySHSIdsh8SH8IhQiJyIwIjUiSCJRIlMitCK2Irgi
3346 IUAhSyFWIWAhbSFvIXEhcyF8IYUhiCGKIY0hjyGRIZYhmCGhIaYhsSHJIdIh2yHxIfwiFCInIjAiNSJI
3347 uiK8Ir4iwCLCIsQixiLIIsoizCLOItAi0yLWItki3CLfIuIi5SLoIusi7iLxIvQi9yL6Iv0jACMDIwYj
3347 IlEiUyK0IrYiuCK6IrwiviLAIsIixCLGIsgiyiLMIs4i0CLTItYi2SLcIt8i4iLlIugi6yLuIvEi9CL3
3348 CSMMIw8jEiMVIxgjGyMeIyEjJCMnIyojLSMwIzMjQCNJI1EjUyNVI1cjfCOEI5gjoyOxI7sjyCPPI9Uj
3348 Ivoi/SMAIwMjBiMJIwwjDyMSIxUjGCMbIx4jISMkIycjKiMtIzAjMyNAI0kjUSNTI1UjVyN4I4AjlCOf
3349 1yPZI94j4CPlI+cj6SPrI/gkBCQHJAokDSQaJBwkKSQ4JDokPCQ+JEYkWCRhJGYkeSSGJIgkiiSMJJ8k
3349 I60jtyPEI8sjzSPPI9Qj1iPbI90j3yPhI/Ij/iQBJAQkByQKJBMkICQvJDEkMyQ1JD0kTyRYJF0kcCR9
3350 qCStJLgk3CTlJOwlBCUTJSAlIiUkJSYlRyVJJUslTSVPJVElUyVgJWMlZiVpJX0lfyWfJawlriWwJbIl
3350 JH8kgSSDJJYknySkJK8kyCTRJNgk8CT/JQwlDiUQJRIlMyU1JTclOSU7JT0lPyVMJU8lUiVVJWQlZiV1
3351 1yXZJdsl3SXfJeEl4yX2JfgmEyYgJiImJCYmJkcmSSZLJk0mTyZRJlMmYCZjJmYmaSZuJnAmfiaLJo0m
3351 JYIlhCWGJYglqSWrJa0lryWxJbMltSXCJcUlyCXLJdgl2iXhJe4l8CXyJfQmGSYfJiEmIyYoJiomLCYu
3352 jyaRJrImtCa2Jrgmuia8Jr4myybOJtEm1CbZJtsm4SbuJvAm8ib0JxUnFycZJx4nICciJyQnJicrJy0n
3352 JjAmPSZAJkMmRiZSJlQmdCaBJoMmhSaHJqgmqiasJq4msCayJrQmwSbEJscmyibPJtEm1ybkJuYm6Cbq
3353 MydAJ0InRCdGJ2cnaSdrJ3Ancid0J3YneCeJJ4wnjyeSJ5UnoSejJ7wnySfLJ80nzyfwJ/In9Cf2J/gn
3353 JwsnDScPJxEnEycVJxcnJCcnJyonLSc8J0snWCdaJ1wnXid/J4EngyeFJ4cniSeLJ5gnmyeeJ6EnuCe6
3354 +if8KAkoDCgPKBIoHiggKDgoRShHKEkoSyhsKG4ocChyKHQodih4KH4ogCiHKJQoliiYKJoovyjBKMMo
3354 J8Qn0SfTJ9Un1yf4J/on/Cf+KAAoAigEKCIoQyhQKFIoVChWKHcoeSh7KH0ofyiBKIMojiiQKJsoqCiq
3355 xSjHKMkoyyjWKPAo/Sj/KQEpAykkKSYpKCkqKSwpLikwKT0pQClDKUYpVCliKXMpgSmDKYUphymJKZIp
3355 KKworijPKNEo0yjVKNco2SjbKPkpEikfKSEpIyklKUYpSClKKUwpTilQKVIpXyliKWUpaCmPKbEpvinA
3356 lCmWKa8puCnBKcgp3ynsKe4p8CnyKhMqFSoXKhkqGyodKh8qJiouKjsqPSo/KkIqYyplKmcqaipsKm4q
3356 KcIpxCnlKecp6SnuKfAp8in0KfYqAyoFKhsqKCoqKiwqLipPKlEqUypVKlcqWSpbKmAqYipwKoEqjyqS
3357 cCqBKoQqhyqKKo0qliqYKq4quyq9KsAqwyrkKuYq6SrrKu0q7yrxKwYrGCslKycrKistK04rUCtTK1Ur
3357 KpQqliqYKqEqoyqlKq4qsCqyKs8q2CrhKugq/ysMKw4rESsUKzkrOys+K0ErQytFK0crVCtWK3oriyuO
3358 VytZK1srZCt9K4orjCuPK5Irsyu1K7gruyu9K78rwSvHK8kr1yvoK+or7CvvK/IsDywRLBQsFiwYLBos
3358 K5ErkyuWK58roSukK70r0SveK+Ar4yvmLAcsCSwMLA8sESwTLBUsLCwuLDksRixILEssTixvLHEsdCx3
3359 HCw0LFQsYSxjLGYsaSyKLIwsjyySLJQsliyZLKYsqSysLK8svizALM8s3CzeLOEs5C0FLQctCi0NLQ8t
3359 LHkseyx+LI8skiyVLJgsmyykLKYsvCzJLMsszizRLPIs9Cz3LPos/Cz+LQAtBS0HLQ0tGi0cLR8tIi1D
3360 ES0TLR4tIC0rLTgtOi09LUAtYS1jLWYtaC1qLWwtbi2FLYstmC2aLZ0toC3BLcMtxi3ILcotzC3PLe0u
3360 LUUtSC1LLU0tTy1RLW4tcC2CLY8tkS2ULZctuC26Lb0twC3CLcQtxy3ULdct2i3dLekt6y4DLhAuEi4V
3361 Di4bLh0uIC4jLkQuRi5JLkwuTi5QLlIuXy5hLmgudS53LnoufS6eLqAuoy6mLqguqi6sLr0uvy7RLt4u
3361 LhguOS47Lj4uQS5DLkUuRy5TLlUubi57Ln0ugC6DLqQupi6pLqwuri6wLrIuty65Lr8uzC7OLtEu1C75
3362 4C7jLuYvBy8JLwwvDy8RLxMvFS8sLy4vOS9GL0gvSy9OL3MvdS94L3svfS9/L4EvjS+PL68vwC/CL8Qv
3362 Lvsu/i8BLwMvBS8ILxUvGC8bLx4vKS8rL0UvUi9UL1cvWi97L30vgC+CL4Qvhi+IL5YvpC+1L7cvuS+8
3363 xy/KL9Mv1S/YL/UwCTAWMBgwGzAeMD8wQTBEMEYwSDBKMEwwUTBeMGswbTBwMHMwlDCWMJkwnDCeMKAw
3363 L78v3C/eL+Ev4y/lL+cv6TABMCEwLjAwMDMwNjBbMGUwZzBpMGwwbzBxMHMwdTCDMIUwlDClMKgwqzCt
3364 ojCnMKkwrzC8ML4wwTDEMOUw5zDqMO0w7zDxMPQxATEEMQcxCjEXMRkxLzFAMUIxRTFIMUoxUzFVMVcx
3364 MK8wvDC+MMEwxDDlMOcw6jDtMO8w8TDzMPkw+zECMRMxFjEYMRoxHTE1MUIxRDFHMUoxazFtMXAxczF1
3365 ZDFmMWkxbDGNMY8xkjGUMZYxmDGaMakxuDHFMccxyjHNMe4x8DHzMfYx+DH6Mf0yCjINMhAyEzIqMiwy
3365 MXcxejGOMZAxsDG9Mb8xwjHFMeYx6DHrMe0x7zHxMfQyBTIIMgsyDjIRMhwyNDJBMkMyRjJJMmoybDJv
3366 NjJDMkUySDJLMmwybjJxMnMydTJ3MnoyizKOMpEylDKXMqIyujLHMskyzDLPMvAy8jL1Mvcy+TL7Mv4z
3366 MnEyczJ1MncyfjKGMpMylTKYMpsyuDK6Mr0yvzLBMsMyxTLXMvAzATMEMwYzCTMMMxUzIjMkMyczKjNL
3367 JTNHM1QzVjNZM1wzfTN/M4IzhTOHM4kzizOPM5EzljOnM6kzqzOtM7AzuTPKM8wzzjPQM9Mz6zP4M/oz
3367 M00zUDNSM1QzVjNZM24zgDONM48zkjOVM7YzuDO7M74zwDPCM8Qz2zPhM+4z8DPzM/Y0FzQZNBw0HjQg
3368 /TQANCU0LzQxNDM0NjQ5NDs0PTQ/NE00TzReNGs0bTRwNHM0lDSWNJk0nDSeNKA0ozTANMI01DThNOM0
3368 NCI0JTQqNDc0RDRGNEk0TDRxNHM0djR5NHs0fTR/NJI0rTS6NLw0vzTCNOM05TToNOs07TTvNPE1AjUE
3369 5jTpNQY1CDULNQ01DzURNRM1JTU+NUs1TTVQNVM1dDV2NXk1ezV9NX81gjWgNbk11jXgNeo2CTYMNg82
3369 NRY1IzUlNSg1KzVMNU41UTVUNVY1WDVaNV41YDVlNXI1dDV3NXo1mzWdNaA1ozWlNac1qTWvNbE1vzXc
3370 EjYVNhc2GjZHNmQ2ezaINpM2ojaxNtY28TcKNx43HzciNyM3JjcnNyo3LTcuNy83MDczNzw3PjdFN0g3
3370 NeY18DYPNhI2FDYXNho2HTYgNk02ajaBNo42mTaoNrc23Db3NxA3JDclNyg3KTcsNy03MDczNzQ3NTc2
3371 SzdON1M3VzddN2Y3aTdsN283gDeGN5E3nTegN6M3pjenN7A3uTe+N9E32jffN+g38zgMOCA4NThCOF84
3371 Nzk3QjdEN0s3TjdRN1Q3WTddN2M3bDdvN3I3dTeGN4w3lzejN6Y3qTesN603tje/N8Q31zfgN+U37jf5
3372 dTh+OIU4nTi6OL04vzjCOMU4yDjLOOc4+zkCOR85IjklOSg5KzktOTA5TzlnOYQ5hzmKOY05kDmTOZY5
3372 OBI4Jjg7OEg4dDiGOKE4qjixOMk45jjpOOw47zjyOPU4+DkUOSg5LzlMOU85UjlVOVg5WjldOXw5lDmx
3373 wjnUOe86DDoPOhE6FDoXOhk6HDo4OkA6UzpcOl87MDsyOzU7ODs6Ozw7PztCO0Q7RztKO007UDtTO1Y7
3373 ObQ5tzm6Ob05vznCOd859ToSOhU6GDobOh46IDojOj86RzpaOmM6Zjs3Ozo7PDs/O0I7RTtHO0o7TTtP
3374 WTtbO147YTtkO2c7aTtrO247cTt0O3c7ejt9O4A7gjuFO4c7ijuNO5A7kzuWO5g7mzueO6E7pDunO6k7
3374 O1I7VTtYO1s7XjthO2M7ZTtnO2k7azttO3A7cjt0O3Y7eDt6O3w7fzuCO4Q7hjuIO4s7jTuQO5I7lTuY
3375 rDuuO7A7sju0O7Y7uDu6O7w7vzvCO8U7xzvKO8070DvSO9U72DvaO9w73jvhO+M75TvoO+o77TvwO/I7
3375 O5o7nTugO6I7pDunO6o7rTuwO7I7tTu4O7s7vjvAO8I7xDvHO8k7zDvOO9E71DvWO9g72zveO+E75Dvm
3376 9Dv2O/g7+zv+PAE8BDwGPAk8DDwPPBI8FDwXPBk8HDwfPCE8IzwlPCg8KjwsPC48MTw0PDc8OTw8PGU8
3376 O+k76zvuO/E78zv1O/g7+zv9PAA8AjwFPAc8CTwMPA88EjwVPBc8GjwdPCA8IzwmPCk8KzwuPDA8Mzw2
3377 bzxxPHM8djx4PHo8fDx+PIE8iDyXPKA8ojynPKo8rDy1PLo84zzlPOg86zztPO888TzzPPY9Aj0LPQ09
3377 PDk8PDw/PEI8azx5PIY8iDyKPIs8jTyOPJA8kjyUPL08xzzJPMw8zzzRPNM81TzYPNs87DzvPPI89Tz4
3378 ED0TPSw9VT1XPVk9XD1ePWA9Yj1kPWc9dT1+PYA9hz2JPYs9jj2fPaI9pT2oPas9tT2+PcA9zz3SPdU9
3378 PP89Dj0XPRk9Hj0hPSQ9RT1HPUo9TD1OPVA9Uz1ePWc9bD14PYE9gz2GPYk9oj3LPc090D3TPdU91z3Z
3379 2D3bPd494T3kPg0+Dz4RPhQ+Fj4YPho+HT4gPjI+Oz49PlQ+Vz5aPl0+YD5jPmY+aT5rPm4+cT50Pp0+
3379 Pds93j4HPgk+Cz4OPhA+Ej4UPhY+GT4wPjk+Oz5EPkc+ST5LPk0+dj54Pno+fT5/PoE+gz6GPok+jj6X
3380 qz64Pro+vD69Pr8+wD7CPsQ+xj7nPuk+7D7vPvE+8z71Pw4/ED85Pzs/PT8+P0A/QT9DP0U/Rz9wP3I/
3380 Ppk+tD63Prk+vD6/PsI+xT7IPso+zT7QPtM+1j7ZPwI/BD8GPwc/CT8KPww/Dj8QPzk/Oz89Pz4/QD9B
3381 dT94P3o/fD9+P4A/gz+MP50/oD+jP6Y/qT+yP7Q/tT/HP/A/8j/0P/U/9z/4P/o//D/+QCdAKUArQCxA
3381 P0M/RT9HP3A/cj91P3g/ej98P34/gD+DP4g/kT+TP54/oT+kP6c/qj+tP9I/1D/XP9o/3D/eP+E/60AQ
3382 LkAvQDFAM0A1QEJAa0BtQG9AckB0QHZAeEB7QH5Ag0CMQI5ApUCoQKtArkCxQLRAtkC5QLxAv0DCQMVA
3382 QBJAFUAXQBlAG0AeQCxAUUBTQFZAWUBbQF1AYEBiQHtAfUCmQKhAqkCtQK9AsUCzQLVAuEDGQM9A0UDY
3383 5kDoQOtA7kDwQPJA9ED4QPpBG0EdQSBBI0ElQSdBKUE0QTZBX0FhQWNBZEFmQWdBaUFrQW1BlkGYQZpB
3383 QNtA3kDgQQlBC0ENQRBBEkEUQRZBGEEbQSJBK0EtQTJBNEE3QUFBSkFMQVtBXkFhQWRBZ0FqQW1BcEGZ
3384 m0GdQZ5BoEGiQaRBzUHPQdFB1EHWQdhB2kHdQeBB5UHuQfBCC0INQg9CEkIVQhhCGkIcQh9CIkIlQihC
3384 QZtBnUGgQaJBpEGmQalBrEG+QcdByUHgQeNB5kHpQexB70HyQfVB+EH6Qf1CAEIpQitCLUIuQjBCMUIz
3385 K0IuQldCWUJbQlxCXkJfQmFCY0JlQo5CkEKSQpNClUKWQphCmkKcQsVCx0LJQsxCzkLQQtJC1ELXQtxC
3385 QjVCN0JYQlpCXUJgQmJCZEJmQn9CgUKqQqxCrkKvQrFCskK0QrZCuELhQuNC5kLpQutC7ULvQvFC9EL9
3386 5ULnQvJC9EL3QvpC/UL/QyRDJkMpQytDLUMvQzFDO0NgQ2JDZUNoQ2pDbENuQ3xDoUOjQ6ZDqUOrQ61D
3386 Qw5DEUMUQxdDGkMjQyVDJkM4Q2FDY0NlQ2ZDaENpQ2tDbUNvQ3xDpUOnQ6lDrEOuQ7BDskO1Q7hDvUPG
3387 r0OxQ8pDzEP1Q/dD+kP9Q/9EAUQDRAVECEQfRChEKkQzRDZEOUQ8RD9EaERqRGxEb0RxRHNEdUR4RHtE
3387 Q8hD30PiQ+VD6EPrQ+5D8EPzQ/ZD+UP8Q/5EH0QhRCREJ0QpRCtELUQxRDNEVERWRFlEXEReRGBEYkRt
3388 gkSLRI1EkkSVRJdEuES6RL1EwETCRMRExkTRRPpE/ET/RQJFBEUGRQhFC0UORRNFHEUeRSNFJkUpRVJF
3388 RG9EmESaRJxEnUSfRKBEokSkRKZEz0TRRNNE1ETWRNdE2UTbRN1FBkUIRQpFDUUPRRFFE0UWRRlFHkUn
3389 VEVWRVlFW0VdRV9FYkVlRWxFdUV3RYBFgkWFRYhFi0W0RbZFuEW5RbtFvEW+RcBFwkXRRfpF/EX/RgJG
3389 RSlFLkUwRTJFW0VdRWBFY0VlRWdFaUVsRW9FdkV/RYFFikWNRZBFk0WWRb9FwUXDRcRFxkXHRclFy0XO
3390 BEYGRghGC0YORhNGHEYeRiFGJEYwRjlGPEcNRw9HEUcTRxVHF0cZRxtHHUcfRyJHJEcmRylHLEcuRzFH
3390 Rd1GBkYIRgpGDUYPRhFGE0YWRhlGHkYnRilGLEYuRjpGQ0ZGRxdHGUcbRx5HIEcjRyVHKEcqRyxHLkcx
3391 NEc2RzhHO0c9R0BHQkdER0dHSUdLR05HUEdSR1RHVkdZR1tHXUdfR2FHY0dlR2dHakdsR25HcEdyR3RH
3391 RzNHNUc3RzlHO0c9Rz9HQkdFR0dHSUdLR01HT0dRR1NHVkdYR1pHXUdfR2FHY0dlR2dHakdsR29HcUd0
3392 dkd4R3tHfUeAR4JHhEeHR4pHjUePR5FHk0eWR5hHmkedR59HoUejR6VHp0epR6tHrUevR7FHtEe2R7hH
3392 R3ZHeEd6R3xHfkeBR4RHhkeJR4tHjkeQR5JHlUeYR5tHnkegR6JHpEemR6hHqketR7BHske1R7dHuUe7
3393 uke8R75HwEfDR8VHyEfKR8xHzkfQR9NH1kfZR9xH30fhR+NH5UfnR+lH60fuR/BH8kf1R/dIAEgDSNZI
3393 R71Hv0fBR8NHxUfHR8lHy0fNR9BH0kfUR9dH2kfdR99H4UfjR+VH50fqR+xH70fxR/NH9Uf3R/pH/UgA
3394 2EjbSN5I4EjiSOVI6EjqSO1I8EjzSPZI+Uj8SP9JAkkESQdJCkkNSQ9JEUkUSRdJGkkdSSBJI0kmSShJ
3394 SAJIBUgOSBFI5EjmSOlI7EjvSPJI9Ej3SPpI/Ej/SQJJBUkISQtJDkkQSRJJFEkWSRhJGkkdSR9JIUkj
3395 K0ktSTBJM0k2STlJPEk+SUFJRElHSUpJTUlPSVJJVElWSVhJWklcSV5JYEliSWVJaElrSW1JcElzSXZJ
3395 SSVJJ0kpSStJLUkwSTNJNUk4STpJPUk/SUJJRUlHSUpJTUlPSVFJVElWSVlJXElfSWJJZElnSWpJbUlv
3396 eEl7SX5JgEmCSYRJh0mJSYtJjkmQSZNJlkmYSZpJnEmeSaFJpEmnSapJrEmvSbJJtEm3SbpJvUm/ScJJ
3396 SXFJc0l1SXhJe0l9SYBJg0mFSYdJikmNSZBJk0mVSZhJmkmdSZ9JokmkSadJqkmsSa9JsUm0SbZJuEm7
3397 xEnHSclJy0nNSdBJ0knUSdZJ2UncSd9J4UnkSe1J8ErDSsZKyUrMSs9K0krVSthK20reSuFK5ErnSupK
3397 Sb5JwUnEScZJyUnMSc9J0knVSdhJ2kndSd9J4knlSehJ60nuSfFJ+kn9StBK00rWStlK3ErfSuJK5Uro
3398 7UrwSvNK9kr5SvxK/0sCSwVLCEsLSw5LEUsUSxdLGksdSyBLI0smSylLLEsvSzJLNUs4SztLPktBS0RL
3398 SutK7krxSvRK90r6Sv1LAEsDSwZLCUsMSw9LEksVSxhLG0seSyFLJEsnSypLLUswSzNLNks5SzxLP0tC
3399 R0tKS01LUEtTS1ZLWUtcS19LYktlS2hLa0tuS3FLdEt3S3pLfUuAS4NLhkuJS4xLj0uSS5VLmEubS55L
3399 S0VLSEtLS05LUUtUS1dLWktdS2BLY0tmS2lLbEtvS3JLdUt4S3tLfkuBS4RLhkuJS4xLj0uSS5VLmEub
3400 oUukS6dLqkutS7BLs0u2S7lLvEu/S8JLxUvIS8tLzkvRS9RL10vaS91L4EvjS+ZL6UvsS+9L8kv1S/hL
3400 S55LoUukS6dLqkutS7BLs0u2S7lLvEu/S8JLxUvIS8tLzkvRS9RL10vaS91L4EvjS+ZL6UvsS+9L8kv1
3401 +0wWTCtMLUxBTFtMdUyZTK5MwUzWTNhM9E0rTVVNcE15TYdNiU2bTZ1NqU3ATeVN6k39TglOLE4/TlhO
3401 S/hL+0v+TAFMBEwHTBJMHkxDTFhMbUyLTKBMukzaTP1NEE0jTS1NNk1STV9NYU1vTXFNiE2hTb1N104B
3402 ZU6BToxOr06zTrVOzE7YTuJPA08YTz1PVk9jT29PlE+uT8JPzk/nT/lQFVAsUEpQZ1B6UJpQplCwUO9R
3402 Tg9OOU5LTmROgE6MTp9Oq07KTuRO+08ATw5PIk9DT09PYU97T55PqE/ET9FP00/oT+xP+FAVUC5QOlBV
3403 DlEaUThRTlFlUXhRi1GfUbdRulHUUfBR/FIKUipSLFI6UlJSaVJ8UqZStFK2UtBS3VMAUwRTEFMvU0RT
3403 UFdQdVCBUI1QpVDKUM5Q0FDrUQFRJlFEUVdRWVFvUYJRwVHlUfJSKVJGUmlSbVKBUo1SoFKzUspS3lLs
3404 UFNpU3dTkVOkU7BTzlPqVABUBFQWVDNUP1RBVEpUTVROVFdUWlRbVGRUZ1WYVZtVnVWgVaNVplWpVatV
3404 UwxTDlMgUzpTRlNJU15TdVOUU65TulPGU95T9VQOVCFUPVQ/VE1UVlRZVFpUY1RmVGdUcFRzVaRVp1Wp
3405 rVWwVbNVtVW4VbtVvlXBVcRVx1XJVcxVz1XRVdRV11XaVd1V4FXjVeVV6FXrVe5V8VX0VfZV+FX6Vf1W
3405 VaxVr1WyVbVVuFW7Vb1VwFXDVcVVyFXLVc1V0FXTVdZV2FXbVd5V4VXjVeZV6VXsVe5V8FXyVfRV9lX4
3406 AFYDVgZWCVYMVg9WEVYUVhdWGlYcVh5WIVYkViZWKFYrVi5WMVY0VjdWOVY7Vj5WQVZEVkdWSlZMVk9W
3406 VftV/VYAVgJWBFYGVghWClYNVhBWElYUVhZWGVYcVh5WIVYkViZWKVYsVi9WMVYzVjZWOVY8Vj5WQVZE
3407 UlZUVlZWWFZaVlxWXlZgVmJWZVZoVmtWblZwVnNWdlZ5VnxWf1aCVoRWh1aKVo1Wj1aRVpNWlVaYVppW
3407 VkdWSlZMVk5WUVZTVlZWWVZcVl5WYVZkVmZWaVZsVm9WcVZ0VnZWeFZ7Vn5WgFaCVoVWh1aKVo1Wj1aR
3408 nFafVqJWpVanVqpWrVawVrNWtla4VrpWvFa+VsBWwlbFVshWy1bOVtBW01bVVthW21bdVuBW41blVuhW
3408 VpRWl1aZVpxWnlahVqRWp1apVqxWrlawVrNWtla4VrpWvVbAVsNWxlbIVstWzVbQVtJW1VbXVtlW21be
3409 6lbtVu9W8lb1VvdW+Vb7Vv5XAVcDVwVXCFcKVwxXD1cSVxVXGFcbVx1XIFciVyVXLlcxWGJYZVhoWGtY
3409 VuFW5FbnVulW7FbvVvJW9Fb3VvpW/VcAVwNXBlcIVwtXDlcQVxNXFlcZVxxXH1ciVyVXJ1cqVy1XMFc5
3410 blhxWHRYd1h6WH1YgFiDWIZYiViMWI9YkliVWJhYm1ieWKFYpFinWKpYrViwWLNYtli5WLxYv1jCWMVY
3410 VzxYbVhwWHNYdlh5WHxYf1iCWIVYiFiLWI5YkViUWJdYmlidWKBYo1imWKlYrFivWLJYtVi4WLtYvljB
3411 yFjLWM5Y0VjUWNdY2ljdWOBY41jmWOlY7FjvWPJY9Vj4WPtY/lkBWQRZB1kKWQ1ZEFkTWRZZGVkcWR9Z
3411 WMRYx1jKWM1Y0FjTWNZY2VjcWN9Y4ljlWOhY61juWPFY9Fj3WPpY/VkAWQNZBlkJWQxZD1kSWRVZGFkb
3412 IlklWShZK1kuWTFZNFk3WTpZPVlAWUNZRllJWUxZT1lSWVVZWFlbWV5ZYVlkWWdZalltWXBZc1l2WXlZ
3412 WR5ZIVkkWSdZKlktWTBZM1k2WTlZPFk/WUJZRVlIWUtZTllRWVRZV1laWV1ZYFljWWZZaVlsWW9Zcll1
3413 fFl/WYJZhVmIWYtZjlmRWZRZl1maWZ1ZoFmjWaZZqVmsWa9Zslm1WbhZu1m+WcFZxFnHWcpZzVnQWdNZ
3413 WXhZe1l+WYFZhFmHWYpZjVmQWZNZllmZWZxZn1miWaVZqFmrWa5ZsVm0WbdZulm9WcBZw1nGWclZzFnP
3414 1lnZWdxZ31niWeVZ6FnrWe5Z8Vn0WfdZ+ln9WgBaA1oGWglaDFoPWhJaFVoYWhtaHlohWiRaJ1oqWi1a
3414 WdJZ1VnYWdtZ3lnhWeRZ51nqWe1Z8FnzWfZZ+Vn8Wf9aAloFWghaC1oOWhFaFFoXWhpaHVogWiNaJlop
3415 L1oyWjRaNlo5WjxaPlpAWkJaRFpGWklaTFpOWlBaUlpUWlZaWFpbWl1aYFpiWmRaZlpoWmtabVpwWnJa
3415 WixaL1oyWjRaN1o5WjtaPVpAWkNaRVpIWkpaTFpOWlBaU1pWWlhaWlpcWl9aYlpkWmZaaVprWm1acFpy
3416 dFp2WnlafFp+WoBaglqEWoZaiFqKWoxaj1qSWpVamFqbWp5aoVqjWqZaqFqqWqxarlqwWrNatlq5Wrta
3416 WnVaeFp6Wn1af1qBWoRah1qKWoxaj1qSWpRallqYWppanVqgWqJapFqmWqhaqlqsWq9asVqzWrxav1rC
3417 vVq/WsFaxFrGWshaylrNWtBa0lrVWtda2lrcWt9a4VrjWuVa51rpWuxa71rxWvRa91r6Wvxa/lsAWwNb
3417 WsVax1rKWs1az1rRWtRa1lrZWtxa31rhWuNa5VrnWula61ruWvBa8lr1Wvda+Vr7Wv1a/1sBWwRbBlsI
3418 BlsIWwpbDFsOWxFbE1sWWxhbGlscWx5bIVsjWyZbKVssWy9bMVs0WzdbOVs7Wz1bP1tCW0VbR1tJW0xb
3418 WwtbDVsPWxJbFVsYWxtbHVsfWyFbI1slWyhbK1stWzBbMls0WzZbOFs7Wz1bQFtCW0VbSFtKW0xbTltR
3419 T1tRW1NbVltZW1tbXlthW2NbZltoW2pbbVtwW3lbe1t+W4Bbg1uGW4hbiluNW49bkluUW5ZbmFuaW6Nb
3419 W1NbVltZW1xbXlthW2NbZVtoW2pbbFtuW3FbdFt2W3hbe1t+W4Bbg1uGW4hbi1uOW5Bbk1uVW5hbmluc
3420 pVumW69bsluzW7xbv1vAW8lbzgAAAAAAAAICAAAAAAAAC8MAAAAAAAAAAAAAAAAAAFvdA</bytes>
3420 W55boVujW6VbrluwW7Fbulu9W75bx1vKW8tb1FvZAAAAAAAAAgIAAAAAAAALwwAAAAAAAAAAAAAAAAAA
3421 W+g</bytes>
3421 </object>
3422 </object>
3422 </data>
3423 </data>
3423 </archive>
3424 </archive>
@@ -28,6 +28,7 b' from zopeinterface import Interface, Attribute, implements, classProvides'
28
28
29 from IPython.kernel.core.history import FrontEndHistory
29 from IPython.kernel.core.history import FrontEndHistory
30 from IPython.kernel.core.util import Bunch
30 from IPython.kernel.core.util import Bunch
31 from IPython.kernel.engineservice import IEngineCore
31
32
32 ##############################################################################
33 ##############################################################################
33 # TEMPORARY!!! fake configuration, while we decide whether to use tconfig or
34 # TEMPORARY!!! fake configuration, while we decide whether to use tconfig or
@@ -284,7 +285,6 b' class FrontEndBase(object):'
284
285
285 def _add_block_id_for_failure(self, failure, blockID):
286 def _add_block_id_for_failure(self, failure, blockID):
286 """_add_block_id_for_failure"""
287 """_add_block_id_for_failure"""
287
288 failure.blockID = blockID
288 failure.blockID = blockID
289 return failure
289 return failure
290
290
@@ -348,6 +348,7 b' class FrontEndBase(object):'
348
348
349
349
350 def render_error(self, failure):
350 def render_error(self, failure):
351 <<<<<<< TREE
351 """Subclasses must override to render the failure.
352 """Subclasses must override to render the failure.
352
353
353 In asynchronous frontends, this method will be called as a
354 In asynchronous frontends, this method will be called as a
@@ -359,4 +360,63 b' class FrontEndBase(object):'
359
360
360
361
361
362
363 =======
364 """Subclasses must override to render the failure. Since this method
365 will be called as a twisted.internet.defer.Deferred's callback,
366 implementations should return result when finished.
367 """
368
369 return failure
370
371
372
373 class AsyncFrontEndBase(FrontEndBase):
374 """
375 Overrides FrontEndBase to wrap execute in a deferred result.
376 All callbacks are made as callbacks on the deferred result.
377 """
378
379 implements(IFrontEnd)
380 classProvides(IFrontEndFactory)
381
382 def __init__(self, engine=None, history=None):
383 assert(engine==None or IEngineCore.providedBy(engine))
384 self.engine = IEngineCore(engine)
385 if history is None:
386 self.history = FrontEndHistory(input_cache=[''])
387 else:
388 self.history = history
389
390
391 def execute(self, block, blockID=None):
392 """Execute the block and return the deferred result.
393
394 Parameters:
395 block : {str, AST}
396 blockID : any
397 Caller may provide an ID to identify this block.
398 result['blockID'] := blockID
399
400 Result:
401 Deferred result of self.interpreter.execute
402 """
403
404 if(not self.is_complete(block)):
405 from twisted.python.failure import Failure
406 return Failure(Exception("Block is not compilable"))
407
408 if(blockID == None):
409 blockID = uuid.uuid4() #random UUID
410
411 d = self.engine.execute(block)
412 d.addCallback(self._add_history, block=block)
413 d.addCallback(self._add_block_id_for_result, blockID)
414 d.addErrback(self._add_block_id_for_failure, blockID)
415 d.addBoth(self.update_cell_prompt, blockID=blockID)
416 d.addCallbacks(self.render_result,
417 errback=self.render_error)
418
419 return d
420
421 >>>>>>> MERGE-SOURCE
362
422
@@ -84,7 +84,7 b' class TestAsyncFrontendBase(unittest.TestCase):'
84 d.addCallback(self.checkBlockID, expected='TEST_ID')
84 d.addCallback(self.checkBlockID, expected='TEST_ID')
85
85
86 def test_blockID_added_to_failure(self):
86 def test_blockID_added_to_failure(self):
87 block = "raise Exception()"
87 block = "raise Exception()"
88
88
89 d = self.fb.execute(block,blockID='TEST_ID')
89 d = self.fb.execute(block,blockID='TEST_ID')
90 d.addErrback(self.checkFailureID, expected='TEST_ID')
90 d.addErrback(self.checkFailureID, expected='TEST_ID')
@@ -97,8 +97,8 b' class IOStream:'
97
97
98 def close(self):
98 def close(self):
99 pass
99 pass
100
100
101
101
102 class IOTerm:
102 class IOTerm:
103 """ Term holds the file or file-like objects for handling I/O operations.
103 """ Term holds the file or file-like objects for handling I/O operations.
104
104
@@ -113,16 +113,16 b' class IOTerm:'
113 self.cin = IOStream(cin,sys.stdin)
113 self.cin = IOStream(cin,sys.stdin)
114 self.cout = IOStream(cout,sys.stdout)
114 self.cout = IOStream(cout,sys.stdout)
115 self.cerr = IOStream(cerr,sys.stderr)
115 self.cerr = IOStream(cerr,sys.stderr)
116
116
117 # Global variable to be used for all I/O
117 # Global variable to be used for all I/O
118 Term = IOTerm()
118 Term = IOTerm()
119
119
120 import IPython.rlineimpl as readline
120 import IPython.rlineimpl as readline
121 # Remake Term to use the readline i/o facilities
121 # Remake Term to use the readline i/o facilities
122 if sys.platform == 'win32' and readline.have_readline:
122 if sys.platform == 'win32' and readline.have_readline:
123
123
124 Term = IOTerm(cout=readline._outputfile,cerr=readline._outputfile)
124 Term = IOTerm(cout=readline._outputfile,cerr=readline._outputfile)
125
125
126
126
127 #****************************************************************************
127 #****************************************************************************
128 # Generic warning/error printer, used by everything else
128 # Generic warning/error printer, used by everything else
@@ -130,9 +130,9 b' def warn(msg,level=2,exit_val=1):'
130 """Standard warning printer. Gives formatting consistency.
130 """Standard warning printer. Gives formatting consistency.
131
131
132 Output is sent to Term.cerr (sys.stderr by default).
132 Output is sent to Term.cerr (sys.stderr by default).
133
133
134 Options:
134 Options:
135
135
136 -level(2): allows finer control:
136 -level(2): allows finer control:
137 0 -> Do nothing, dummy function.
137 0 -> Do nothing, dummy function.
138 1 -> Print message.
138 1 -> Print message.
@@ -142,7 +142,7 b' def warn(msg,level=2,exit_val=1):'
142
142
143 -exit_val (1): exit value returned by sys.exit() for a level 4
143 -exit_val (1): exit value returned by sys.exit() for a level 4
144 warning. Ignored for all other levels."""
144 warning. Ignored for all other levels."""
145
145
146 if level>0:
146 if level>0:
147 header = ['','','WARNING: ','ERROR: ','FATAL ERROR: ']
147 header = ['','','WARNING: ','ERROR: ','FATAL ERROR: ']
148 print >> Term.cerr, '%s%s' % (header[level],msg)
148 print >> Term.cerr, '%s%s' % (header[level],msg)
@@ -167,10 +167,10 b' def fatal(msg,exit_val=1):'
167
167
168 #---------------------------------------------------------------------------
168 #---------------------------------------------------------------------------
169 # Debugging routines
169 # Debugging routines
170 #
170 #
171 def debugx(expr,pre_msg=''):
171 def debugx(expr,pre_msg=''):
172 """Print the value of an expression from the caller's frame.
172 """Print the value of an expression from the caller's frame.
173
173
174 Takes an expression, evaluates it in the caller's frame and prints both
174 Takes an expression, evaluates it in the caller's frame and prints both
175 the given expression and the resulting value (as well as a debug mark
175 the given expression and the resulting value (as well as a debug mark
176 indicating the name of the calling function. The input must be of a form
176 indicating the name of the calling function. The input must be of a form
@@ -178,7 +178,7 b" def debugx(expr,pre_msg=''):"
178
178
179 An optional message can be passed, which will be prepended to the printed
179 An optional message can be passed, which will be prepended to the printed
180 expr->value pair."""
180 expr->value pair."""
181
181
182 cf = sys._getframe(1)
182 cf = sys._getframe(1)
183 print '[DBG:%s] %s%s -> %r' % (cf.f_code.co_name,pre_msg,expr,
183 print '[DBG:%s] %s%s -> %r' % (cf.f_code.co_name,pre_msg,expr,
184 eval(expr,cf.f_globals,cf.f_locals))
184 eval(expr,cf.f_globals,cf.f_locals))
@@ -200,18 +200,18 b' try:'
200 Return the *USER* CPU time in seconds since the start of the process.
200 Return the *USER* CPU time in seconds since the start of the process.
201 This is done via a call to resource.getrusage, so it avoids the
201 This is done via a call to resource.getrusage, so it avoids the
202 wraparound problems in time.clock()."""
202 wraparound problems in time.clock()."""
203
203
204 return resource.getrusage(resource.RUSAGE_SELF)[0]
204 return resource.getrusage(resource.RUSAGE_SELF)[0]
205
205
206 def clocks():
206 def clocks():
207 """clocks() -> floating point number
207 """clocks() -> floating point number
208
208
209 Return the *SYSTEM* CPU time in seconds since the start of the process.
209 Return the *SYSTEM* CPU time in seconds since the start of the process.
210 This is done via a call to resource.getrusage, so it avoids the
210 This is done via a call to resource.getrusage, so it avoids the
211 wraparound problems in time.clock()."""
211 wraparound problems in time.clock()."""
212
212
213 return resource.getrusage(resource.RUSAGE_SELF)[1]
213 return resource.getrusage(resource.RUSAGE_SELF)[1]
214
214
215 def clock():
215 def clock():
216 """clock() -> floating point number
216 """clock() -> floating point number
217
217
@@ -219,9 +219,9 b' try:'
219 the process. This is done via a call to resource.getrusage, so it
219 the process. This is done via a call to resource.getrusage, so it
220 avoids the wraparound problems in time.clock()."""
220 avoids the wraparound problems in time.clock()."""
221
221
222 u,s = resource.getrusage(resource.RUSAGE_SELF)[:2]
222 u,s = resource.getrusage(resource.RUSAGE_SELF)[:2]
223 return u+s
223 return u+s
224
224
225 def clock2():
225 def clock2():
226 """clock2() -> (t_user,t_system)
226 """clock2() -> (t_user,t_system)
227
227
@@ -247,7 +247,7 b' def timings_out(reps,func,*args,**kw):'
247 Under Unix, the return value is the sum of user+system time consumed by
247 Under Unix, the return value is the sum of user+system time consumed by
248 the process, computed via the resource module. This prevents problems
248 the process, computed via the resource module. This prevents problems
249 related to the wraparound effect which the time.clock() function has.
249 related to the wraparound effect which the time.clock() function has.
250
250
251 Under Windows the return value is in wall clock seconds. See the
251 Under Windows the return value is in wall clock seconds. See the
252 documentation for the time module for more details."""
252 documentation for the time module for more details."""
253
253
@@ -310,7 +310,7 b" def system(cmd,verbose=0,debug=0,header=''):"
310 Options:
310 Options:
311
311
312 - verbose (0): print the command to be executed.
312 - verbose (0): print the command to be executed.
313
313
314 - debug (0): only print, do not actually execute.
314 - debug (0): only print, do not actually execute.
315
315
316 - header (''): Header to print on screen prior to the executed command (it
316 - header (''): Header to print on screen prior to the executed command (it
@@ -334,15 +334,15 b' def abbrev_cwd():'
334 if len(cwd) < 4:
334 if len(cwd) < 4:
335 return cwd
335 return cwd
336 drivepart,tail = os.path.splitdrive(cwd)
336 drivepart,tail = os.path.splitdrive(cwd)
337
338
337
339 parts = tail.split('/')
338
339 parts = tail.split('/')
340 if len(parts) > 2:
340 if len(parts) > 2:
341 tail = '/'.join(parts[-2:])
341 tail = '/'.join(parts[-2:])
342
342
343 return (drivepart + (
343 return (drivepart + (
344 cwd == '/' and '/' or tail))
344 cwd == '/' and '/' or tail))
345
345
346
346
347 # This function is used by ipython in a lot of places to make system calls.
347 # This function is used by ipython in a lot of places to make system calls.
348 # We need it to be slightly different under win32, due to the vagaries of
348 # We need it to be slightly different under win32, due to the vagaries of
@@ -354,7 +354,7 b" def shell(cmd,verbose=0,debug=0,header=''):"
354 Options:
354 Options:
355
355
356 - verbose (0): print the command to be executed.
356 - verbose (0): print the command to be executed.
357
357
358 - debug (0): only print, do not actually execute.
358 - debug (0): only print, do not actually execute.
359
359
360 - header (''): Header to print on screen prior to the executed command (it
360 - header (''): Header to print on screen prior to the executed command (it
@@ -368,7 +368,7 b" def shell(cmd,verbose=0,debug=0,header=''):"
368 if verbose or debug: print header+cmd
368 if verbose or debug: print header+cmd
369 # flush stdout so we don't mangle python's buffering
369 # flush stdout so we don't mangle python's buffering
370 sys.stdout.flush()
370 sys.stdout.flush()
371
371
372 if not debug:
372 if not debug:
373 platutils.set_term_title("IPy " + cmd)
373 platutils.set_term_title("IPy " + cmd)
374 os.system(cmd)
374 os.system(cmd)
@@ -406,10 +406,10 b" def getoutput(cmd,verbose=0,debug=0,header='',split=0):"
406
406
407 Note: a stateful version of this function is available through the
407 Note: a stateful version of this function is available through the
408 SystemExec class.
408 SystemExec class.
409
409
410 This is pretty much deprecated and rarely used,
410 This is pretty much deprecated and rarely used,
411 genutils.getoutputerror may be what you need.
411 genutils.getoutputerror may be what you need.
412
412
413 """
413 """
414
414
415 if verbose or debug: print header+cmd
415 if verbose or debug: print header+cmd
@@ -461,7 +461,7 b' class SystemExec:'
461
461
462 Note: here we refer to the system and getoutput functions from this
462 Note: here we refer to the system and getoutput functions from this
463 library, not the ones from the standard python library.
463 library, not the ones from the standard python library.
464
464
465 This class offers the system and getoutput functions as methods, but the
465 This class offers the system and getoutput functions as methods, but the
466 verbose, debug and header parameters can be set for the instance (at
466 verbose, debug and header parameters can be set for the instance (at
467 creation time or later) so that they don't need to be specified on each
467 creation time or later) so that they don't need to be specified on each
@@ -476,13 +476,9 b' class SystemExec:'
476 - bq: alias to getoutput
476 - bq: alias to getoutput
477
477
478 An instance can then be created as:
478 An instance can then be created as:
479 >>> sysexec = SystemExec(verbose=1,debug=0,header='Calling: ')
479 >>> sysexec = SystemExec(verbose=1,debug=0,header='Calling: ')
480
481 And used as:
482 >>> sysexec.xsys('pwd')
483 >>> dirlist = sysexec.bq('ls -l')
484 """
480 """
485
481
486 def __init__(self,verbose=0,debug=0,header='',split=0):
482 def __init__(self,verbose=0,debug=0,header='',split=0):
487 """Specify the instance's values for verbose, debug and header."""
483 """Specify the instance's values for verbose, debug and header."""
488 setattr_list(self,'verbose debug header split')
484 setattr_list(self,'verbose debug header split')
@@ -634,7 +630,7 b" def process_cmdline(argv,names=[],defaults={},usage=''):"
634 Arguments:
630 Arguments:
635
631
636 - argv: list of arguments, typically sys.argv.
632 - argv: list of arguments, typically sys.argv.
637
633
638 - names: list of option names. See DPyGetOpt docs for details on options
634 - names: list of option names. See DPyGetOpt docs for details on options
639 syntax.
635 syntax.
640
636
@@ -656,7 +652,7 b" def process_cmdline(argv,names=[],defaults={},usage=''):"
656
652
657 defaults.update(getopt.optionValues)
653 defaults.update(getopt.optionValues)
658 args = getopt.freeValues
654 args = getopt.freeValues
659
655
660 return defaults,args
656 return defaults,args
661
657
662 #----------------------------------------------------------------------------
658 #----------------------------------------------------------------------------
@@ -684,8 +680,7 b' def optstr2types(ostr):'
684
680
685 #----------------------------------------------------------------------------
681 #----------------------------------------------------------------------------
686 def read_dict(filename,type_conv=None,**opt):
682 def read_dict(filename,type_conv=None,**opt):
687
683 r"""Read a dictionary of key=value pairs from an input file, optionally
688 """Read a dictionary of key=value pairs from an input file, optionally
689 performing conversions on the resulting values.
684 performing conversions on the resulting values.
690
685
691 read_dict(filename,type_conv,**opt) -> dict
686 read_dict(filename,type_conv,**opt) -> dict
@@ -731,20 +726,33 b' def read_dict(filename,type_conv=None,**opt):'
731 to make a list of all appearances.
726 to make a list of all appearances.
732
727
733 Example:
728 Example:
734 If the input file test.ini has:
735 i 3
736 x 4.5
737 y 5.5
738 s hi ho
739 Then:
740
729
730 If the input file test.ini contains (we put it in a string to keep the test
731 self-contained):
732
733 >>> test_ini = '''\
734 ... i 3
735 ... x 4.5
736 ... y 5.5
737 ... s hi ho'''
738
739 Then we can use it as follows:
741 >>> type_conv={int:'i',float:'x',None:'s'}
740 >>> type_conv={int:'i',float:'x',None:'s'}
742 >>> read_dict('test.ini')
741
743 {'i': '3', 's': 'hi ho', 'x': '4.5', 'y': '5.5'}
742 >>> d = read_dict(test_ini)
744 >>> read_dict('test.ini',type_conv)
743
745 {'i': 3, 's': 'hi ho', 'x': 4.5, 'y': '5.5'}
744 >>> sorted(d.items())
746 >>> read_dict('test.ini',type_conv,purge=1)
745 [('i', '3'), ('s', 'hi ho'), ('x', '4.5'), ('y', '5.5')]
747 {'i': 3, 's': 'hi ho', 'x': 4.5}
746
747 >>> d = read_dict(test_ini,type_conv)
748
749 >>> sorted(d.items())
750 [('i', 3), ('s', 'hi ho'), ('x', 4.5), ('y', '5.5')]
751
752 >>> d = read_dict(test_ini,type_conv,purge=True)
753
754 >>> sorted(d.items())
755 [('i', 3), ('s', 'hi ho'), ('x', 4.5)]
748 """
756 """
749
757
750 # starting config
758 # starting config
@@ -762,9 +770,15 b' def read_dict(filename,type_conv=None,**opt):'
762 raise ValueError, 'Unique keys must be given as a string, List or Tuple'
770 raise ValueError, 'Unique keys must be given as a string, List or Tuple'
763
771
764 dict = {}
772 dict = {}
773
765 # first read in table of values as strings
774 # first read in table of values as strings
766 file = open(filename,'r')
775 if '\n' in filename:
767 for line in file.readlines():
776 lines = filename.splitlines()
777 file = None
778 else:
779 file = open(filename,'r')
780 lines = file.readlines()
781 for line in lines:
768 line = line.strip()
782 line = line.strip()
769 if len(line) and line[0]=='#': continue
783 if len(line) and line[0]=='#': continue
770 if len(line)>0:
784 if len(line)>0:
@@ -831,7 +845,7 b' def flag_calls(func):'
831
845
832 Testing for truth in wrapper.called allows you to determine if a call to
846 Testing for truth in wrapper.called allows you to determine if a call to
833 func() was attempted and succeeded."""
847 func() was attempted and succeeded."""
834
848
835 def wrapper(*args,**kw):
849 def wrapper(*args,**kw):
836 wrapper.called = False
850 wrapper.called = False
837 out = func(*args,**kw)
851 out = func(*args,**kw)
@@ -853,7 +867,7 b' def dhook_wrap(func,*a,**k):'
853 """
867 """
854
868
855 def f(*a,**k):
869 def f(*a,**k):
856
870
857 dhook_s = sys.displayhook
871 dhook_s = sys.displayhook
858 sys.displayhook = sys.__displayhook__
872 sys.displayhook = sys.__displayhook__
859 try:
873 try:
@@ -873,10 +887,10 b' def doctest_reload():'
873 This routine:
887 This routine:
874
888
875 - reloads doctest
889 - reloads doctest
876
890
877 - resets its global 'master' attribute to None, so that multiple uses of
891 - resets its global 'master' attribute to None, so that multiple uses of
878 the module interactively don't produce cumulative reports.
892 the module interactively don't produce cumulative reports.
879
893
880 - Monkeypatches its core test runner method to protect it from IPython's
894 - Monkeypatches its core test runner method to protect it from IPython's
881 modified displayhook. Doctest expects the default displayhook behavior
895 modified displayhook. Doctest expects the default displayhook behavior
882 deep down, so our modification breaks it completely. For this reason, a
896 deep down, so our modification breaks it completely. For this reason, a
@@ -910,16 +924,16 b' def get_home_dir():'
910
924
911 isdir = os.path.isdir
925 isdir = os.path.isdir
912 env = os.environ
926 env = os.environ
913
927
914 # first, check py2exe distribution root directory for _ipython.
928 # first, check py2exe distribution root directory for _ipython.
915 # This overrides all. Normally does not exist.
929 # This overrides all. Normally does not exist.
916
930
917 if '\\library.zip\\' in IPython.__file__.lower():
931 if '\\library.zip\\' in IPython.__file__.lower():
918 root, rest = IPython.__file__.lower().split('library.zip')
932 root, rest = IPython.__file__.lower().split('library.zip')
919 if isdir(root + '_ipython'):
933 if isdir(root + '_ipython'):
920 os.environ["IPYKITROOT"] = root.rstrip('\\')
934 os.environ["IPYKITROOT"] = root.rstrip('\\')
921 return root
935 return root
922
936
923 try:
937 try:
924 homedir = env['HOME']
938 homedir = env['HOME']
925 if not isdir(homedir):
939 if not isdir(homedir):
@@ -977,7 +991,7 b' class LSString(str):'
977 .n (or .nlstr): original value (the string itself).
991 .n (or .nlstr): original value (the string itself).
978 .s (or .spstr): value as whitespace-separated string.
992 .s (or .spstr): value as whitespace-separated string.
979 .p (or .paths): list of path objects
993 .p (or .paths): list of path objects
980
994
981 Any values which require transformations are computed only once and
995 Any values which require transformations are computed only once and
982 cached.
996 cached.
983
997
@@ -1013,14 +1027,14 b' class LSString(str):'
1013 except AttributeError:
1027 except AttributeError:
1014 self.__paths = [path(p) for p in self.split('\n') if os.path.exists(p)]
1028 self.__paths = [path(p) for p in self.split('\n') if os.path.exists(p)]
1015 return self.__paths
1029 return self.__paths
1016
1030
1017 p = paths = property(get_paths)
1031 p = paths = property(get_paths)
1018
1032
1019 def print_lsstring(arg):
1033 def print_lsstring(arg):
1020 """ Prettier (non-repr-like) and more informative printer for LSString """
1034 """ Prettier (non-repr-like) and more informative printer for LSString """
1021 print "LSString (.p, .n, .l, .s available). Value:"
1035 print "LSString (.p, .n, .l, .s available). Value:"
1022 print arg
1036 print arg
1023
1037
1024 print_lsstring = result_display.when_type(LSString)(print_lsstring)
1038 print_lsstring = result_display.when_type(LSString)(print_lsstring)
1025
1039
1026 #----------------------------------------------------------------------------
1040 #----------------------------------------------------------------------------
@@ -1033,7 +1047,7 b' class SList(list):'
1033 .n (or .nlstr): value as a string, joined on newlines.
1047 .n (or .nlstr): value as a string, joined on newlines.
1034 .s (or .spstr): value as a string, joined on spaces.
1048 .s (or .spstr): value as a string, joined on spaces.
1035 .p (or .paths): list of path objects
1049 .p (or .paths): list of path objects
1036
1050
1037 Any values which require transformations are computed only once and
1051 Any values which require transformations are computed only once and
1038 cached."""
1052 cached."""
1039
1053
@@ -1059,32 +1073,32 b' class SList(list):'
1059 return self.__nlstr
1073 return self.__nlstr
1060
1074
1061 n = nlstr = property(get_nlstr)
1075 n = nlstr = property(get_nlstr)
1062
1076
1063 def get_paths(self):
1077 def get_paths(self):
1064 try:
1078 try:
1065 return self.__paths
1079 return self.__paths
1066 except AttributeError:
1080 except AttributeError:
1067 self.__paths = [path(p) for p in self if os.path.exists(p)]
1081 self.__paths = [path(p) for p in self if os.path.exists(p)]
1068 return self.__paths
1082 return self.__paths
1069
1083
1070 p = paths = property(get_paths)
1084 p = paths = property(get_paths)
1071
1085
1072 def grep(self, pattern, prune = False, field = None):
1086 def grep(self, pattern, prune = False, field = None):
1073 """ Return all strings matching 'pattern' (a regex or callable)
1087 """ Return all strings matching 'pattern' (a regex or callable)
1074
1088
1075 This is case-insensitive. If prune is true, return all items
1089 This is case-insensitive. If prune is true, return all items
1076 NOT matching the pattern.
1090 NOT matching the pattern.
1077
1091
1078 If field is specified, the match must occur in the specified
1092 If field is specified, the match must occur in the specified
1079 whitespace-separated field.
1093 whitespace-separated field.
1080
1094
1081 Examples::
1095 Examples::
1082
1096
1083 a.grep( lambda x: x.startswith('C') )
1097 a.grep( lambda x: x.startswith('C') )
1084 a.grep('Cha.*log', prune=1)
1098 a.grep('Cha.*log', prune=1)
1085 a.grep('chm', field=-1)
1099 a.grep('chm', field=-1)
1086 """
1100 """
1087
1101
1088 def match_target(s):
1102 def match_target(s):
1089 if field is None:
1103 if field is None:
1090 return s
1104 return s
@@ -1094,7 +1108,7 b' class SList(list):'
1094 return tgt
1108 return tgt
1095 except IndexError:
1109 except IndexError:
1096 return ""
1110 return ""
1097
1111
1098 if isinstance(pattern, basestring):
1112 if isinstance(pattern, basestring):
1099 pred = lambda x : re.search(pattern, x, re.IGNORECASE)
1113 pred = lambda x : re.search(pattern, x, re.IGNORECASE)
1100 else:
1114 else:
@@ -1105,25 +1119,25 b' class SList(list):'
1105 return SList([el for el in self if not pred(match_target(el))])
1119 return SList([el for el in self if not pred(match_target(el))])
1106 def fields(self, *fields):
1120 def fields(self, *fields):
1107 """ Collect whitespace-separated fields from string list
1121 """ Collect whitespace-separated fields from string list
1108
1122
1109 Allows quick awk-like usage of string lists.
1123 Allows quick awk-like usage of string lists.
1110
1124
1111 Example data (in var a, created by 'a = !ls -l')::
1125 Example data (in var a, created by 'a = !ls -l')::
1112 -rwxrwxrwx 1 ville None 18 Dec 14 2006 ChangeLog
1126 -rwxrwxrwx 1 ville None 18 Dec 14 2006 ChangeLog
1113 drwxrwxrwx+ 6 ville None 0 Oct 24 18:05 IPython
1127 drwxrwxrwx+ 6 ville None 0 Oct 24 18:05 IPython
1114
1128
1115 a.fields(0) is ['-rwxrwxrwx', 'drwxrwxrwx+']
1129 a.fields(0) is ['-rwxrwxrwx', 'drwxrwxrwx+']
1116 a.fields(1,0) is ['1 -rwxrwxrwx', '6 drwxrwxrwx+']
1130 a.fields(1,0) is ['1 -rwxrwxrwx', '6 drwxrwxrwx+']
1117 (note the joining by space).
1131 (note the joining by space).
1118 a.fields(-1) is ['ChangeLog', 'IPython']
1132 a.fields(-1) is ['ChangeLog', 'IPython']
1119
1133
1120 IndexErrors are ignored.
1134 IndexErrors are ignored.
1121
1135
1122 Without args, fields() just split()'s the strings.
1136 Without args, fields() just split()'s the strings.
1123 """
1137 """
1124 if len(fields) == 0:
1138 if len(fields) == 0:
1125 return [el.split() for el in self]
1139 return [el.split() for el in self]
1126
1140
1127 res = SList()
1141 res = SList()
1128 for el in [f.split() for f in self]:
1142 for el in [f.split() for f in self]:
1129 lineparts = []
1143 lineparts = []
@@ -1135,18 +1149,18 b' class SList(list):'
1135 pass
1149 pass
1136 if lineparts:
1150 if lineparts:
1137 res.append(" ".join(lineparts))
1151 res.append(" ".join(lineparts))
1138
1152
1139 return res
1153 return res
1140 def sort(self,field= None, nums = False):
1154 def sort(self,field= None, nums = False):
1141 """ sort by specified fields (see fields())
1155 """ sort by specified fields (see fields())
1142
1156
1143 Example::
1157 Example::
1144 a.sort(1, nums = True)
1158 a.sort(1, nums = True)
1145
1159
1146 Sorts a by second field, in numerical order (so that 21 > 3)
1160 Sorts a by second field, in numerical order (so that 21 > 3)
1147
1161
1148 """
1162 """
1149
1163
1150 #decorate, sort, undecorate
1164 #decorate, sort, undecorate
1151 if field is not None:
1165 if field is not None:
1152 dsu = [[SList([line]).fields(field), line] for line in self]
1166 dsu = [[SList([line]).fields(field), line] for line in self]
@@ -1160,8 +1174,8 b' class SList(list):'
1160 except ValueError:
1174 except ValueError:
1161 n = 0;
1175 n = 0;
1162 dsu[i][0] = n
1176 dsu[i][0] = n
1163
1177
1164
1178
1165 dsu.sort()
1179 dsu.sort()
1166 return SList([t[1] for t in dsu])
1180 return SList([t[1] for t in dsu])
1167
1181
@@ -1171,9 +1185,9 b' def print_slist(arg):'
1171 if hasattr(arg, 'hideonce') and arg.hideonce:
1185 if hasattr(arg, 'hideonce') and arg.hideonce:
1172 arg.hideonce = False
1186 arg.hideonce = False
1173 return
1187 return
1174
1188
1175 nlprint(arg)
1189 nlprint(arg)
1176
1190
1177 print_slist = result_display.when_type(SList)(print_slist)
1191 print_slist = result_display.when_type(SList)(print_slist)
1178
1192
1179
1193
@@ -1187,14 +1201,14 b' def esc_quotes(strng):'
1187 #----------------------------------------------------------------------------
1201 #----------------------------------------------------------------------------
1188 def make_quoted_expr(s):
1202 def make_quoted_expr(s):
1189 """Return string s in appropriate quotes, using raw string if possible.
1203 """Return string s in appropriate quotes, using raw string if possible.
1190
1204
1191 Effectively this turns string: cd \ao\ao\
1205 Effectively this turns string: cd \ao\ao\
1192 to: r"cd \ao\ao\_"[:-1]
1206 to: r"cd \ao\ao\_"[:-1]
1193
1207
1194 Note the use of raw string and padding at the end to allow trailing backslash.
1208 Note the use of raw string and padding at the end to allow trailing backslash.
1195
1209
1196 """
1210 """
1197
1211
1198 tail = ''
1212 tail = ''
1199 tailpadding = ''
1213 tailpadding = ''
1200 raw = ''
1214 raw = ''
@@ -1245,7 +1259,7 b" def raw_input_multi(header='', ps1='==> ', ps2='..> ',terminate_str = '.'):"
1245 while new_line.endswith('\\'):
1259 while new_line.endswith('\\'):
1246 new_line = new_line[:-1] + raw_input(ps2)
1260 new_line = new_line[:-1] + raw_input(ps2)
1247 lines.append(new_line)
1261 lines.append(new_line)
1248
1262
1249 return lines[:-1] # don't return the termination command
1263 return lines[:-1] # don't return the termination command
1250 except EOFError:
1264 except EOFError:
1251 print
1265 print
@@ -1287,7 +1301,7 b' def ask_yes_no(prompt,default=None):'
1287 print
1301 print
1288 else:
1302 else:
1289 raise
1303 raise
1290
1304
1291 return answers[ans]
1305 return answers[ans]
1292
1306
1293 #----------------------------------------------------------------------------
1307 #----------------------------------------------------------------------------
@@ -1306,9 +1320,12 b' class EvalDict:'
1306 Emulate a dict which evaluates its contents in the caller's frame.
1320 Emulate a dict which evaluates its contents in the caller's frame.
1307
1321
1308 Usage:
1322 Usage:
1309 >>>number = 19
1323 >>> number = 19
1310 >>>text = "python"
1324
1311 >>>print "%(text.capitalize())s %(number/9.0).1f rules!" % EvalDict()
1325 >>> text = "python"
1326
1327 >>> print "%(text.capitalize())s %(number/9.0).1f rules!" % EvalDict()
1328 Python 2.1 rules!
1312 """
1329 """
1313
1330
1314 # This version is due to sismex01@hebmex.com on c.l.py, and is basically a
1331 # This version is due to sismex01@hebmex.com on c.l.py, and is basically a
@@ -1328,14 +1345,19 b' def qw(words,flat=0,sep=None,maxsplit=-1):'
1328 qw(words,flat=0,sep=' ',maxsplit=-1) -> words.split(sep,maxsplit)
1345 qw(words,flat=0,sep=' ',maxsplit=-1) -> words.split(sep,maxsplit)
1329
1346
1330 words can also be a list itself, and with flat=1, the output will be
1347 words can also be a list itself, and with flat=1, the output will be
1331 recursively flattened. Examples:
1348 recursively flattened.
1349
1350 Examples:
1332
1351
1333 >>> qw('1 2')
1352 >>> qw('1 2')
1334 ['1', '2']
1353 ['1', '2']
1354
1335 >>> qw(['a b','1 2',['m n','p q']])
1355 >>> qw(['a b','1 2',['m n','p q']])
1336 [['a', 'b'], ['1', '2'], [['m', 'n'], ['p', 'q']]]
1356 [['a', 'b'], ['1', '2'], [['m', 'n'], ['p', 'q']]]
1357
1337 >>> qw(['a b','1 2',['m n','p q']],flat=1)
1358 >>> qw(['a b','1 2',['m n','p q']],flat=1)
1338 ['a', 'b', '1', '2', 'm', 'n', 'p', 'q'] """
1359 ['a', 'b', '1', '2', 'm', 'n', 'p', 'q']
1360 """
1339
1361
1340 if type(words) in StringTypes:
1362 if type(words) in StringTypes:
1341 return [word.strip() for word in words.split(sep,maxsplit)
1363 return [word.strip() for word in words.split(sep,maxsplit)
@@ -1415,7 +1437,7 b' def igrep(pat,list):'
1415 #----------------------------------------------------------------------------
1437 #----------------------------------------------------------------------------
1416 def indent(str,nspaces=4,ntabs=0):
1438 def indent(str,nspaces=4,ntabs=0):
1417 """Indent a string a given number of spaces or tabstops.
1439 """Indent a string a given number of spaces or tabstops.
1418
1440
1419 indent(str,nspaces=4,ntabs=0) -> indent str by ntabs+nspaces.
1441 indent(str,nspaces=4,ntabs=0) -> indent str by ntabs+nspaces.
1420 """
1442 """
1421 if str is None:
1443 if str is None:
@@ -1426,7 +1448,7 b' def indent(str,nspaces=4,ntabs=0):'
1426 return outstr[:-len(ind)]
1448 return outstr[:-len(ind)]
1427 else:
1449 else:
1428 return outstr
1450 return outstr
1429
1451
1430 #-----------------------------------------------------------------------------
1452 #-----------------------------------------------------------------------------
1431 def native_line_ends(filename,backup=1):
1453 def native_line_ends(filename,backup=1):
1432 """Convert (in-place) a file to line-ends native to the current OS.
1454 """Convert (in-place) a file to line-ends native to the current OS.
@@ -1452,13 +1474,13 b' def native_line_ends(filename,backup=1):'
1452 os.remove(bak_filename)
1474 os.remove(bak_filename)
1453 except:
1475 except:
1454 pass
1476 pass
1455
1477
1456 #----------------------------------------------------------------------------
1478 #----------------------------------------------------------------------------
1457 def get_pager_cmd(pager_cmd = None):
1479 def get_pager_cmd(pager_cmd = None):
1458 """Return a pager command.
1480 """Return a pager command.
1459
1481
1460 Makes some attempts at finding an OS-correct one."""
1482 Makes some attempts at finding an OS-correct one."""
1461
1483
1462 if os.name == 'posix':
1484 if os.name == 'posix':
1463 default_pager_cmd = 'less -r' # -r for color control sequences
1485 default_pager_cmd = 'less -r' # -r for color control sequences
1464 elif os.name in ['nt','dos']:
1486 elif os.name in ['nt','dos']:
@@ -1570,7 +1592,7 b' def page(strng,start=0,screen_lines=0,pager_cmd = None):'
1570 return
1592 return
1571 except IPython.ipapi.TryNext:
1593 except IPython.ipapi.TryNext:
1572 pass
1594 pass
1573
1595
1574 # Ugly kludge, but calling curses.initscr() flat out crashes in emacs
1596 # Ugly kludge, but calling curses.initscr() flat out crashes in emacs
1575 TERM = os.environ.get('TERM','dumb')
1597 TERM = os.environ.get('TERM','dumb')
1576 if TERM in ['dumb','emacs'] and os.name != 'nt':
1598 if TERM in ['dumb','emacs'] and os.name != 'nt':
@@ -1581,7 +1603,7 b' def page(strng,start=0,screen_lines=0,pager_cmd = None):'
1581 str_toprint = os.linesep.join(str_lines)
1603 str_toprint = os.linesep.join(str_lines)
1582 num_newlines = len(str_lines)
1604 num_newlines = len(str_lines)
1583 len_str = len(str_toprint)
1605 len_str = len(str_toprint)
1584
1606
1585 # Dumb heuristics to guesstimate number of on-screen lines the string
1607 # Dumb heuristics to guesstimate number of on-screen lines the string
1586 # takes. Very basic, but good enough for docstrings in reasonable
1608 # takes. Very basic, but good enough for docstrings in reasonable
1587 # terminals. If someone later feels like refining it, it's not hard.
1609 # terminals. If someone later feels like refining it, it's not hard.
@@ -1738,7 +1760,7 b' def uniq_stable(elems):'
1738 Note: All elements in the input must be valid dictionary keys for this
1760 Note: All elements in the input must be valid dictionary keys for this
1739 routine to work, as it internally uses a dictionary for efficiency
1761 routine to work, as it internally uses a dictionary for efficiency
1740 reasons."""
1762 reasons."""
1741
1763
1742 unique = []
1764 unique = []
1743 unique_dict = {}
1765 unique_dict = {}
1744 for nn in elems:
1766 for nn in elems:
@@ -1753,13 +1775,13 b' class NLprinter:'
1753
1775
1754 An instance of this class called nlprint is available and callable as a
1776 An instance of this class called nlprint is available and callable as a
1755 function.
1777 function.
1756
1778
1757 nlprint(list,indent=' ',sep=': ') -> prints indenting each level by 'indent'
1779 nlprint(list,indent=' ',sep=': ') -> prints indenting each level by 'indent'
1758 and using 'sep' to separate the index from the value. """
1780 and using 'sep' to separate the index from the value. """
1759
1781
1760 def __init__(self):
1782 def __init__(self):
1761 self.depth = 0
1783 self.depth = 0
1762
1784
1763 def __call__(self,lst,pos='',**kw):
1785 def __call__(self,lst,pos='',**kw):
1764 """Prints the nested list numbering levels."""
1786 """Prints the nested list numbering levels."""
1765 kw.setdefault('indent',' ')
1787 kw.setdefault('indent',' ')
@@ -1772,7 +1794,7 b' class NLprinter:'
1772 stop = kw['stop']; del kw['stop']
1794 stop = kw['stop']; del kw['stop']
1773 if self.depth == 0 and 'header' in kw.keys():
1795 if self.depth == 0 and 'header' in kw.keys():
1774 print kw['header']
1796 print kw['header']
1775
1797
1776 for idx in range(start,stop):
1798 for idx in range(start,stop):
1777 elem = lst[idx]
1799 elem = lst[idx]
1778 if type(elem)==type([]):
1800 if type(elem)==type([]):
@@ -1804,20 +1826,6 b' def sort_compare(lst1,lst2,inplace = 1):'
1804 return lst1 == lst2
1826 return lst1 == lst2
1805
1827
1806 #----------------------------------------------------------------------------
1828 #----------------------------------------------------------------------------
1807 def mkdict(**kwargs):
1808 """Return a dict from a keyword list.
1809
1810 It's just syntactic sugar for making ditcionary creation more convenient:
1811 # the standard way
1812 >>>data = { 'red' : 1, 'green' : 2, 'blue' : 3 }
1813 # a cleaner way
1814 >>>data = dict(red=1, green=2, blue=3)
1815
1816 If you need more than this, look at the Struct() class."""
1817
1818 return kwargs
1819
1820 #----------------------------------------------------------------------------
1821 def list2dict(lst):
1829 def list2dict(lst):
1822 """Takes a list of (key,value) pairs and turns it into a dict."""
1830 """Takes a list of (key,value) pairs and turns it into a dict."""
1823
1831
@@ -1935,7 +1943,7 b' def getattr_list(obj,alist,*args):'
1935 raise ValueError,'getattr_list() takes only one optional argument'
1943 raise ValueError,'getattr_list() takes only one optional argument'
1936 else:
1944 else:
1937 return map(lambda attr: getattr(obj,attr),alist)
1945 return map(lambda attr: getattr(obj,attr),alist)
1938
1946
1939 #----------------------------------------------------------------------------
1947 #----------------------------------------------------------------------------
1940 def map_method(method,object_list,*argseq,**kw):
1948 def map_method(method,object_list,*argseq,**kw):
1941 """map_method(method,object_list,*args,**kw) -> list
1949 """map_method(method,object_list,*args,**kw) -> list
@@ -1987,7 +1995,7 b' def dir2(obj):'
1987 are later not really valid for attribute access (many extension libraries
1995 are later not really valid for attribute access (many extension libraries
1988 have such bugs).
1996 have such bugs).
1989 """
1997 """
1990
1998
1991 # Start building the attribute list via dir(), and then complete it
1999 # Start building the attribute list via dir(), and then complete it
1992 # with a few extra special-purpose calls.
2000 # with a few extra special-purpose calls.
1993 words = dir(obj)
2001 words = dir(obj)
@@ -2064,7 +2072,7 b' def popkey(dct,key,default=NotGiven):'
2064
2072
2065 def wrap_deprecated(func, suggest = '<nothing>'):
2073 def wrap_deprecated(func, suggest = '<nothing>'):
2066 def newFunc(*args, **kwargs):
2074 def newFunc(*args, **kwargs):
2067 warnings.warn("Call to deprecated function %s, use %s instead" %
2075 warnings.warn("Call to deprecated function %s, use %s instead" %
2068 ( func.__name__, suggest),
2076 ( func.__name__, suggest),
2069 category=DeprecationWarning,
2077 category=DeprecationWarning,
2070 stacklevel = 2)
2078 stacklevel = 2)
@@ -2098,7 +2106,7 b' def num_cpus():'
2098 If it can't find a sensible answer, it returns 1 (though an error *may* make
2106 If it can't find a sensible answer, it returns 1 (though an error *may* make
2099 it return a large positive number that's actually incorrect).
2107 it return a large positive number that's actually incorrect).
2100 """
2108 """
2101
2109
2102 # Many thanks to the Parallel Python project (http://www.parallelpython.com)
2110 # Many thanks to the Parallel Python project (http://www.parallelpython.com)
2103 # for the names of the keys we needed to look up for this function. This
2111 # for the names of the keys we needed to look up for this function. This
2104 # code was inspired by their equivalent function.
2112 # code was inspired by their equivalent function.
@@ -1,4 +1,4 b''
1 ''' IPython customization API
1 """IPython customization API
2
2
3 Your one-stop module for configuring & extending ipython
3 Your one-stop module for configuring & extending ipython
4
4
@@ -29,50 +29,50 b' import IPython.ipapi'
29 ip = IPython.ipapi.get()
29 ip = IPython.ipapi.get()
30
30
31 def ankka_f(self, arg):
31 def ankka_f(self, arg):
32 print "Ankka",self,"says uppercase:",arg.upper()
32 print 'Ankka',self,'says uppercase:',arg.upper()
33
33
34 ip.expose_magic("ankka",ankka_f)
34 ip.expose_magic('ankka',ankka_f)
35
35
36 ip.magic('alias sayhi echo "Testing, hi ok"')
36 ip.magic('alias sayhi echo "Testing, hi ok"')
37 ip.magic('alias helloworld echo "Hello world"')
37 ip.magic('alias helloworld echo "Hello world"')
38 ip.system('pwd')
38 ip.system('pwd')
39
39
40 ip.ex('import re')
40 ip.ex('import re')
41 ip.ex("""
41 ip.ex('''
42 def funcci(a,b):
42 def funcci(a,b):
43 print a+b
43 print a+b
44 print funcci(3,4)
44 print funcci(3,4)
45 """)
45 ''')
46 ip.ex("funcci(348,9)")
46 ip.ex('funcci(348,9)')
47
47
48 def jed_editor(self,filename, linenum=None):
48 def jed_editor(self,filename, linenum=None):
49 print "Calling my own editor, jed ... via hook!"
49 print 'Calling my own editor, jed ... via hook!'
50 import os
50 import os
51 if linenum is None: linenum = 0
51 if linenum is None: linenum = 0
52 os.system('jed +%d %s' % (linenum, filename))
52 os.system('jed +%d %s' % (linenum, filename))
53 print "exiting jed"
53 print 'exiting jed'
54
54
55 ip.set_hook('editor',jed_editor)
55 ip.set_hook('editor',jed_editor)
56
56
57 o = ip.options
57 o = ip.options
58 o.autocall = 2 # FULL autocall mode
58 o.autocall = 2 # FULL autocall mode
59
59
60 print "done!"
60 print 'done!'
61 '''
61 """
62
63 #-----------------------------------------------------------------------------
64 # Modules and globals
62
65
63 # stdlib imports
66 # stdlib imports
64 import __builtin__
67 import __builtin__
65 import sys
68 import sys
66
69
67 try: # Python 2.3 compatibility
70 # contains the most recently instantiated IPApi
68 set
71 _RECENT_IP = None
69 except NameError:
72
70 import sets
73 #-----------------------------------------------------------------------------
71 set = sets.Set
74 # Code begins
72
75
73 # our own
74 #from IPython.genutils import warn,error
75
76 class TryNext(Exception):
76 class TryNext(Exception):
77 """Try next hook exception.
77 """Try next hook exception.
78
78
@@ -86,6 +86,7 b' class TryNext(Exception):'
86 self.args = args
86 self.args = args
87 self.kwargs = kwargs
87 self.kwargs = kwargs
88
88
89
89 class UsageError(Exception):
90 class UsageError(Exception):
90 """ Error in magic function arguments, etc.
91 """ Error in magic function arguments, etc.
91
92
@@ -93,6 +94,7 b' class UsageError(Exception):'
93 nevertheless interrupt a macro / batch file.
94 nevertheless interrupt a macro / batch file.
94 """
95 """
95
96
97
96 class IPyAutocall:
98 class IPyAutocall:
97 """ Instances of this class are always autocalled
99 """ Instances of this class are always autocalled
98
100
@@ -109,8 +111,6 b' class IPyAutocall:'
109 self._ip = ip
111 self._ip = ip
110
112
111
113
112 # contains the most recently instantiated IPApi
113
114 class IPythonNotRunning:
114 class IPythonNotRunning:
115 """Dummy do-nothing class.
115 """Dummy do-nothing class.
116
116
@@ -144,8 +144,6 b' class IPythonNotRunning:'
144 """Dummy function, which doesn't do anything and emits no warnings."""
144 """Dummy function, which doesn't do anything and emits no warnings."""
145 pass
145 pass
146
146
147 _recent = None
148
149
147
150 def get(allow_dummy=False,dummy_warn=True):
148 def get(allow_dummy=False,dummy_warn=True):
151 """Get an IPApi object.
149 """Get an IPApi object.
@@ -159,12 +157,13 b' def get(allow_dummy=False,dummy_warn=True):'
159 can be imported as normal modules. You can then direct all the
157 can be imported as normal modules. You can then direct all the
160 configuration operations against the returned object.
158 configuration operations against the returned object.
161 """
159 """
162 global _recent
160 global _RECENT_IP
163 if allow_dummy and not _recent:
161 if allow_dummy and not _RECENT_IP:
164 _recent = IPythonNotRunning(dummy_warn)
162 _RECENT_IP = IPythonNotRunning(dummy_warn)
165 return _recent
163 return _RECENT_IP
164
166
165
167 class IPApi:
166 class IPApi(object):
168 """ The actual API class for configuring IPython
167 """ The actual API class for configuring IPython
169
168
170 You should do all of the IPython configuration by getting an IPApi object
169 You should do all of the IPython configuration by getting an IPApi object
@@ -173,6 +172,8 b' class IPApi:'
173
172
174 def __init__(self,ip):
173 def __init__(self,ip):
175
174
175 global _RECENT_IP
176
176 # All attributes exposed here are considered to be the public API of
177 # All attributes exposed here are considered to be the public API of
177 # IPython. As needs dictate, some of these may be wrapped as
178 # IPython. As needs dictate, some of these may be wrapped as
178 # properties.
179 # properties.
@@ -201,8 +202,7 b' class IPApi:'
201
202
202 self.dbg = DebugTools(self)
203 self.dbg = DebugTools(self)
203
204
204 global _recent
205 _RECENT_IP = self
205 _recent = self
206
206
207 # Use a property for some things which are added to the instance very
207 # Use a property for some things which are added to the instance very
208 # late. I don't have time right now to disentangle the initialization
208 # late. I don't have time right now to disentangle the initialization
@@ -218,8 +218,8 b' class IPApi:'
218 """All configurable variables."""
218 """All configurable variables."""
219
219
220 # catch typos by disabling new attribute creation. If new attr creation
220 # catch typos by disabling new attribute creation. If new attr creation
221 # is in fact wanted (e.g. when exposing new options), do allow_new_attr(True)
221 # is in fact wanted (e.g. when exposing new options), do
222 # for the received rc struct.
222 # allow_new_attr(True) for the received rc struct.
223
223
224 self.IP.rc.allow_new_attr(False)
224 self.IP.rc.allow_new_attr(False)
225 return self.IP.rc
225 return self.IP.rc
@@ -227,22 +227,23 b' class IPApi:'
227 options = property(get_options,None,None,get_options.__doc__)
227 options = property(get_options,None,None,get_options.__doc__)
228
228
229 def expose_magic(self,magicname, func):
229 def expose_magic(self,magicname, func):
230 ''' Expose own function as magic function for ipython
230 """Expose own function as magic function for ipython
231
231
232 def foo_impl(self,parameter_s=''):
232 def foo_impl(self,parameter_s=''):
233 """My very own magic!. (Use docstrings, IPython reads them)."""
233 'My very own magic!. (Use docstrings, IPython reads them).'
234 print 'Magic function. Passed parameter is between < >: <'+parameter_s+'>'
234 print 'Magic function. Passed parameter is between < >:'
235 print '<%s>' % parameter_s
235 print 'The self object is:',self
236 print 'The self object is:',self
236
237
237 ipapi.expose_magic("foo",foo_impl)
238 ipapi.expose_magic('foo',foo_impl)
238 '''
239 """
239
240
240 import new
241 import new
241 im = new.instancemethod(func,self.IP, self.IP.__class__)
242 im = new.instancemethod(func,self.IP, self.IP.__class__)
242 old = getattr(self.IP, "magic_" + magicname, None)
243 old = getattr(self.IP, "magic_" + magicname, None)
243 if old:
244 if old:
244 self.dbg.debug_stack("Magic redefinition '%s', old %s" % (magicname,
245 self.dbg.debug_stack("Magic redefinition '%s', old %s" %
245 old))
246 (magicname,old) )
246
247
247 setattr(self.IP, "magic_" + magicname, im)
248 setattr(self.IP, "magic_" + magicname, im)
248
249
@@ -267,10 +268,10 b' class IPApi:'
267 def cleanup_ipy_script(script):
268 def cleanup_ipy_script(script):
268 """ Make a script safe for _ip.runlines()
269 """ Make a script safe for _ip.runlines()
269
270
270 - Removes empty lines
271 - Removes empty lines Suffixes all indented blocks that end with
271 - Suffixes all indented blocks that end with unindented lines with empty lines
272 - unindented lines with empty lines
272
273 """
273 """
274
274 res = []
275 res = []
275 lines = script.splitlines()
276 lines = script.splitlines()
276
277
@@ -290,7 +291,8 b' class IPApi:'
290 s.startswith('finally')):
291 s.startswith('finally')):
291 return True
292 return True
292
293
293 if level > 0 and newlevel == 0 and not is_secondary_block_start(stripped):
294 if level > 0 and newlevel == 0 and \
295 not is_secondary_block_start(stripped):
294 # add empty line
296 # add empty line
295 res.append('')
297 res.append('')
296
298
@@ -303,8 +305,9 b' class IPApi:'
303 else:
305 else:
304 script = '\n'.join(lines)
306 script = '\n'.join(lines)
305 clean=cleanup_ipy_script(script)
307 clean=cleanup_ipy_script(script)
306 # print "_ip.runlines() script:\n",clean #dbg
308 # print "_ip.runlines() script:\n",clean # dbg
307 self.IP.runlines(clean)
309 self.IP.runlines(clean)
310
308 def to_user_ns(self,vars, interactive = True):
311 def to_user_ns(self,vars, interactive = True):
309 """Inject a group of variables into the IPython user namespace.
312 """Inject a group of variables into the IPython user namespace.
310
313
@@ -392,7 +395,6 b' class IPApi:'
392 for name,val in vdict.iteritems():
395 for name,val in vdict.iteritems():
393 config_ns[name] = val
396 config_ns[name] = val
394
397
395
396 def expand_alias(self,line):
398 def expand_alias(self,line):
397 """ Expand an alias in the command line
399 """ Expand an alias in the command line
398
400
@@ -425,11 +427,9 b' class IPApi:'
425
427
426 self.dbg.check_hotname(name)
428 self.dbg.check_hotname(name)
427
429
428
429 if name in self.IP.alias_table:
430 if name in self.IP.alias_table:
430 self.dbg.debug_stack("Alias redefinition: '%s' => '%s' (old '%s')" %
431 self.dbg.debug_stack("Alias redefinition: '%s' => '%s' (old '%s')"
431 (name, cmd, self.IP.alias_table[name]))
432 % (name, cmd, self.IP.alias_table[name]))
432
433
433
434 if callable(cmd):
434 if callable(cmd):
435 self.IP.alias_table[name] = cmd
435 self.IP.alias_table[name] = cmd
@@ -440,8 +440,8 b' class IPApi:'
440 if isinstance(cmd,basestring):
440 if isinstance(cmd,basestring):
441 nargs = cmd.count('%s')
441 nargs = cmd.count('%s')
442 if nargs>0 and cmd.find('%l')>=0:
442 if nargs>0 and cmd.find('%l')>=0:
443 raise Exception('The %s and %l specifiers are mutually exclusive '
443 raise Exception('The %s and %l specifiers are mutually '
444 'in alias definitions.')
444 'exclusive in alias definitions.')
445
445
446 self.IP.alias_table[name] = (nargs,cmd)
446 self.IP.alias_table[name] = (nargs,cmd)
447 return
447 return
@@ -494,8 +494,8 b' class IPApi:'
494
494
495 - run init_ipython(ip)
495 - run init_ipython(ip)
496 - run ipython_firstrun(ip)
496 - run ipython_firstrun(ip)
497
498 """
497 """
498
499 if mod in self.extensions:
499 if mod in self.extensions:
500 # just to make sure we don't init it twice
500 # just to make sure we don't init it twice
501 # note that if you 'load' a module that has already been
501 # note that if you 'load' a module that has already been
@@ -545,6 +545,7 b' class DebugTools:'
545 if name in self.hotnames:
545 if name in self.hotnames:
546 self.debug_stack( "HotName '%s' caught" % name)
546 self.debug_stack( "HotName '%s' caught" % name)
547
547
548
548 def launch_new_instance(user_ns = None,shellclass = None):
549 def launch_new_instance(user_ns = None,shellclass = None):
549 """ Make and start a new ipython instance.
550 """ Make and start a new ipython instance.
550
551
@@ -212,13 +212,7 b' class InteractiveShell(object,Magic):'
212
212
213 # log system
213 # log system
214 self.logger = Logger(self,logfname='ipython_log.py',logmode='rotate')
214 self.logger = Logger(self,logfname='ipython_log.py',logmode='rotate')
215
215
216 # some minimal strict typechecks. For some core data structures, I
217 # want actual basic python types, not just anything that looks like
218 # one. This is especially true for namespaces.
219 for ns in (user_ns,user_global_ns):
220 if ns is not None and type(ns) != types.DictType:
221 raise TypeError,'namespace must be a dictionary'
222 # Job manager (for jobs run as background threads)
216 # Job manager (for jobs run as background threads)
223 self.jobs = BackgroundJobManager()
217 self.jobs = BackgroundJobManager()
224
218
@@ -1007,10 +1001,17 b' class InteractiveShell(object,Magic):'
1007
1001
1008 Simple usage example:
1002 Simple usage example:
1009
1003
1010 In [1]: x = 'hello'
1004 In [7]: x = 'hello'
1005
1006 In [8]: x
1007 Out[8]: 'hello'
1011
1008
1012 In [2]: __IP.complete('x.l')
1009 In [9]: print x
1013 Out[2]: ['x.ljust', 'x.lower', 'x.lstrip']"""
1010 hello
1011
1012 In [10]: _ip.IP.complete('x.l')
1013 Out[10]: ['x.ljust', 'x.lower', 'x.lstrip'] # random
1014 """
1014
1015
1015 complete = self.Completer.complete
1016 complete = self.Completer.complete
1016 state = 0
1017 state = 0
@@ -1026,6 +1027,8 b' class InteractiveShell(object,Magic):'
1026 state += 1
1027 state += 1
1027 outcomps = comps.keys()
1028 outcomps = comps.keys()
1028 outcomps.sort()
1029 outcomps.sort()
1030 #print "T:",text,"OC:",outcomps # dbg
1031 #print "vars:",self.user_ns.keys()
1029 return outcomps
1032 return outcomps
1030
1033
1031 def set_completer_frame(self, frame=None):
1034 def set_completer_frame(self, frame=None):
@@ -1636,6 +1639,7 b' want to merge them back into the new files.""" % locals()'
1636 # previous call (which most likely existed in a separate scope).
1639 # previous call (which most likely existed in a separate scope).
1637 local_varnames = local_ns.keys()
1640 local_varnames = local_ns.keys()
1638 self.user_ns.update(local_ns)
1641 self.user_ns.update(local_ns)
1642 #self.user_ns['local_ns'] = local_ns # dbg
1639
1643
1640 # Patch for global embedding to make sure that things don't overwrite
1644 # Patch for global embedding to make sure that things don't overwrite
1641 # user globals accidentally. Thanks to Richard <rxe@renre-europe.com>
1645 # user globals accidentally. Thanks to Richard <rxe@renre-europe.com>
@@ -2362,14 +2366,15 b' want to merge them back into the new files.""" % locals()'
2362 def handle_auto(self, line_info):
2366 def handle_auto(self, line_info):
2363 """Hande lines which can be auto-executed, quoting if requested."""
2367 """Hande lines which can be auto-executed, quoting if requested."""
2364
2368
2365 #print 'pre <%s> iFun <%s> rest <%s>' % (pre,iFun,theRest) # dbg
2366 line = line_info.line
2369 line = line_info.line
2367 iFun = line_info.iFun
2370 iFun = line_info.iFun
2368 theRest = line_info.theRest
2371 theRest = line_info.theRest
2369 pre = line_info.pre
2372 pre = line_info.pre
2370 continue_prompt = line_info.continue_prompt
2373 continue_prompt = line_info.continue_prompt
2371 obj = line_info.ofind(self)['obj']
2374 obj = line_info.ofind(self)['obj']
2372
2375
2376 #print 'pre <%s> iFun <%s> rest <%s>' % (pre,iFun,theRest) # dbg
2377
2373 # This should only be active for single-line input!
2378 # This should only be active for single-line input!
2374 if continue_prompt:
2379 if continue_prompt:
2375 self.log(line,line,continue_prompt)
2380 self.log(line,line,continue_prompt)
@@ -88,8 +88,8 b' class Struct:'
88 initialization): keys can't be numbers. But numeric keys can be used and
88 initialization): keys can't be numbers. But numeric keys can be used and
89 accessed using the dictionary syntax. Again, an example:
89 accessed using the dictionary syntax. Again, an example:
90
90
91 This doesn't work:
91 This doesn't work (prompt changed to avoid confusing the test system):
92 >>> s=Struct(4='hi') #doctest: +IGNORE_EXCEPTION_DETAIL
92 ->> s=Struct(4='hi')
93 Traceback (most recent call last):
93 Traceback (most recent call last):
94 ...
94 ...
95 SyntaxError: keyword can't be an expression
95 SyntaxError: keyword can't be an expression
@@ -27,7 +27,7 b' from IPython.kernel import codeutil'
27 from IPython.kernel.clientconnector import ClientConnector
27 from IPython.kernel.clientconnector import ClientConnector
28
28
29 # Other things that the user will need
29 # Other things that the user will need
30 from IPython.kernel.task import Task
30 from IPython.kernel.task import MapTask, StringTask
31 from IPython.kernel.error import CompositeError
31 from IPython.kernel.error import CompositeError
32
32
33 #-------------------------------------------------------------------------------
33 #-------------------------------------------------------------------------------
@@ -44,7 +44,7 b' from IPython.kernel import codeutil'
44 import IPython.kernel.magic
44 import IPython.kernel.magic
45
45
46 # Other things that the user will need
46 # Other things that the user will need
47 from IPython.kernel.task import Task
47 from IPython.kernel.task import MapTask, StringTask
48 from IPython.kernel.error import CompositeError
48 from IPython.kernel.error import CompositeError
49
49
50 #-------------------------------------------------------------------------------
50 #-------------------------------------------------------------------------------
@@ -48,7 +48,7 b' def strip_whitespace(source,require_remote=True):'
48 """strip leading whitespace from input source.
48 """strip leading whitespace from input source.
49
49
50 :Parameters:
50 :Parameters:
51
51
52 """
52 """
53 remote_mark = 'remote()'
53 remote_mark = 'remote()'
54 # Expand tabs to avoid any confusion.
54 # Expand tabs to avoid any confusion.
@@ -101,7 +101,7 b' def strip_whitespace(source,require_remote=True):'
101 class RemoteContextBase(object):
101 class RemoteContextBase(object):
102 def __init__(self):
102 def __init__(self):
103 self.ip = ipapi.get()
103 self.ip = ipapi.get()
104
104
105 def _findsource_file(self,f):
105 def _findsource_file(self,f):
106 linecache.checkcache()
106 linecache.checkcache()
107 s = findsource(f.f_code)
107 s = findsource(f.f_code)
@@ -113,10 +113,10 b' class RemoteContextBase(object):'
113 from IPython import ipapi
113 from IPython import ipapi
114 self.ip = ipapi.get()
114 self.ip = ipapi.get()
115 buf = self.ip.IP.input_hist_raw[-1].splitlines()[1:]
115 buf = self.ip.IP.input_hist_raw[-1].splitlines()[1:]
116 wsource = [l+'\n' for l in buf ]
116 wsource = [l+'\n' for l in buf ]
117
117
118 return strip_whitespace(wsource)
118 return strip_whitespace(wsource)
119
119
120 def findsource(self,frame):
120 def findsource(self,frame):
121 local_ns = frame.f_locals
121 local_ns = frame.f_locals
122 global_ns = frame.f_globals
122 global_ns = frame.f_globals
@@ -128,7 +128,7 b' class RemoteContextBase(object):'
128
128
129 def __enter__(self):
129 def __enter__(self):
130 raise NotImplementedError
130 raise NotImplementedError
131
131
132 def __exit__ (self, etype, value, tb):
132 def __exit__ (self, etype, value, tb):
133 if issubclass(etype,error.StopLocalExecution):
133 if issubclass(etype,error.StopLocalExecution):
134 return True
134 return True
@@ -141,38 +141,3 b' class RemoteMultiEngine(RemoteContextBase):'
141 def __enter__(self):
141 def __enter__(self):
142 src = self.findsource(sys._getframe(1))
142 src = self.findsource(sys._getframe(1))
143 return self.mec.execute(src)
143 return self.mec.execute(src)
144
145
146 # XXX - Temporary hackish testing, we'll move this into proper tests right
147 # away
148
149 if __name__ == '__main__':
150
151 # XXX - for now, we need a running cluster to be started separately. The
152 # daemon work is almost finished, and will make much of this unnecessary.
153 from IPython.kernel import client
154 mec = client.MultiEngineClient(('127.0.0.1',10105))
155
156 try:
157 mec.get_ids()
158 except ConnectionRefusedError:
159 import os, time
160 os.system('ipcluster -n 2 &')
161 time.sleep(2)
162 mec = client.MultiEngineClient(('127.0.0.1',10105))
163
164 mec.block = False
165
166 import itertools
167 c = itertools.count()
168
169 parallel = RemoteMultiEngine(mec)
170
171 with parallel as pr:
172 # A comment
173 remote() # this means the code below only runs remotely
174 print 'Hello remote world'
175 x = 3.14
176 # Comments are OK
177 # Even misindented.
178 y = x+1
@@ -226,29 +226,31 b' class IEngineThreaded(zi.Interface):'
226 class StrictDict(dict):
226 class StrictDict(dict):
227 """This is a strict copying dictionary for use as the interface to the
227 """This is a strict copying dictionary for use as the interface to the
228 properties of an Engine.
228 properties of an Engine.
229
229 :IMPORTANT:
230 :IMPORTANT:
230 This object copies the values you set to it, and returns copies to you
231 This object copies the values you set to it, and returns copies to you
231 when you request them. The only way to change properties os explicitly
232 when you request them. The only way to change properties os explicitly
232 through the setitem and getitem of the dictionary interface.
233 through the setitem and getitem of the dictionary interface.
233 Example:
234
234 >>> e = kernel.get_engine(id)
235 Example:
235 >>> L = someList
236 >>> e = get_engine(id)
237 >>> L = [1,2,3]
236 >>> e.properties['L'] = L
238 >>> e.properties['L'] = L
237 >>> L == e.properties['L']
239 >>> L == e.properties['L']
238 ... True
240 True
239 >>> L.append(something Else)
241 >>> L.append(99)
240 >>> L == e.properties['L']
242 >>> L == e.properties['L']
241 ... False
243 False
244
245 Note that getitem copies, so calls to methods of objects do not affect
246 the properties, as seen here:
242
247
243 getitem copies, so calls to methods of objects do not affect the
244 properties, as in the following example:
245 >>> e.properties[1] = range(2)
248 >>> e.properties[1] = range(2)
246 >>> print e.properties[1]
249 >>> print e.properties[1]
247 ... [0, 1]
250 [0, 1]
248 >>> e.properties[1].append(2)
251 >>> e.properties[1].append(2)
249 >>> print e.properties[1]
252 >>> print e.properties[1]
250 ... [0, 1]
253 [0, 1]
251
252 """
254 """
253 def __init__(self, *args, **kwargs):
255 def __init__(self, *args, **kwargs):
254 dict.__init__(self, *args, **kwargs)
256 dict.__init__(self, *args, **kwargs)
@@ -395,6 +397,7 b' class EngineService(object, service.Service):'
395
397
396 return d
398 return d
397
399
400
398 # The IEngine methods. See the interface for documentation.
401 # The IEngine methods. See the interface for documentation.
399
402
400 def execute(self, lines):
403 def execute(self, lines):
@@ -862,6 +865,30 b' class ThreadedEngineService(EngineService):'
862 def __init__(self, shellClass=Interpreter, mpi=None):
865 def __init__(self, shellClass=Interpreter, mpi=None):
863 EngineService.__init__(self, shellClass, mpi)
866 EngineService.__init__(self, shellClass, mpi)
864
867
868 def wrapped_execute(self, msg, lines):
869 """Wrap self.shell.execute to add extra information to tracebacks"""
870
871 try:
872 result = self.shell.execute(lines)
873 except Exception,e:
874 # This gives the following:
875 # et=exception class
876 # ev=exception class instance
877 # tb=traceback object
878 et,ev,tb = sys.exc_info()
879 # This call adds attributes to the exception value
880 et,ev,tb = self.shell.formatTraceback(et,ev,tb,msg)
881 # Add another attribute
882
883 # Create a new exception with the new attributes
884 e = et(ev._ipython_traceback_text)
885 e._ipython_engine_info = msg
886
887 # Re-raise
888 raise e
889
890 return result
891
865
892
866 def execute(self, lines):
893 def execute(self, lines):
867 # Only import this if we are going to use this class
894 # Only import this if we are going to use this class
@@ -871,6 +898,6 b' class ThreadedEngineService(EngineService):'
871 'method':'execute',
898 'method':'execute',
872 'args':[lines]}
899 'args':[lines]}
873
900
874 d = threads.deferToThread(self.shell.execute, lines)
901 d = threads.deferToThread(self.wrapped_execute, msg, lines)
875 d.addCallback(self.addIDToResult)
902 d.addCallback(self.addIDToResult)
876 return d
903 return d
@@ -79,7 +79,7 b" def magic_px(self,parameter_s=''):"
79 except AttributeError:
79 except AttributeError:
80 print NO_ACTIVE_CONTROLLER
80 print NO_ACTIVE_CONTROLLER
81 else:
81 else:
82 print "Executing command on Controller"
82 print "Parallel execution on engines: %s" % activeController.targets
83 result = activeController.execute(parameter_s)
83 result = activeController.execute(parameter_s)
84 return result
84 return result
85
85
@@ -115,7 +115,7 b' class RoundRobinMap(Map):'
115 # result.append(concat[i:totalLength:maxPartitionLength])
115 # result.append(concat[i:totalLength:maxPartitionLength])
116 return self.concatenate(listOfPartitions)
116 return self.concatenate(listOfPartitions)
117
117
118 styles = {'basic':Map}
118 dists = {'b':Map}
119
119
120
120
121
121
@@ -653,67 +653,55 b' components.registerAdapter(SynchronousMultiEngine, IMultiEngine, ISynchronousMul'
653 class IMultiEngineCoordinator(Interface):
653 class IMultiEngineCoordinator(Interface):
654 """Methods that work on multiple engines explicitly."""
654 """Methods that work on multiple engines explicitly."""
655
655
656 def scatter(key, seq, style='basic', flatten=False, targets='all'):
656 def scatter(key, seq, dist='b', flatten=False, targets='all'):
657 """Partition and distribute a sequence to targets.
657 """Partition and distribute a sequence to targets."""
658
658
659 :Parameters:
659 def gather(key, dist='b', targets='all'):
660 key : str
660 """Gather object key from targets."""
661 The variable name to call the scattered sequence.
662 seq : list, tuple, array
663 The sequence to scatter. The type should be preserved.
664 style : string
665 A specification of how the sequence is partitioned. Currently
666 only 'basic' is implemented.
667 flatten : boolean
668 Should single element sequences be converted to scalars.
669 """
670
671 def gather(key, style='basic', targets='all'):
672 """Gather object key from targets.
673
661
674 :Parameters:
662 def raw_map(func, seqs, dist='b', targets='all'):
675 key : string
676 The name of a sequence on the targets to gather.
677 style : string
678 A specification of how the sequence is partitioned. Currently
679 only 'basic' is implemented.
680 """
663 """
681
664 A parallelized version of Python's builtin `map` function.
682 def map(func, seq, style='basic', targets='all'):
683 """A parallelized version of Python's builtin map.
684
665
685 This function implements the following pattern:
666 This has a slightly different syntax than the builtin `map`.
667 This is needed because we need to have keyword arguments and thus
668 can't use *args to capture all the sequences. Instead, they must
669 be passed in a list or tuple.
686
670
687 1. The sequence seq is scattered to the given targets.
671 The equivalence is:
688 2. map(functionSource, seq) is called on each engine.
689 3. The resulting sequences are gathered back to the local machine.
690
691 :Parameters:
692 targets : int, list or 'all'
693 The engine ids the action will apply to. Call `get_ids` to see
694 a list of currently available engines.
695 func : str, function
696 An actual function object or a Python string that names a
697 callable defined on the engines.
698 seq : list, tuple or numpy array
699 The local sequence to be scattered.
700 style : str
701 Only 'basic' is supported for now.
702
703 :Returns: A list of len(seq) with functionSource called on each element
704 of seq.
705
706 Example
707 =======
708
672
709 >>> rc.mapAll('lambda x: x*x', range(10000))
673 raw_map(func, seqs) -> map(func, seqs[0], seqs[1], ...)
710 [0,2,4,9,25,36,...]
674
675 Most users will want to use parallel functions or the `mapper`
676 and `map` methods for an API that follows that of the builtin
677 `map`.
711 """
678 """
712
679
713
680
714 class ISynchronousMultiEngineCoordinator(IMultiEngineCoordinator):
681 class ISynchronousMultiEngineCoordinator(IMultiEngineCoordinator):
715 """Methods that work on multiple engines explicitly."""
682 """Methods that work on multiple engines explicitly."""
716 pass
683
684 def scatter(key, seq, dist='b', flatten=False, targets='all', block=True):
685 """Partition and distribute a sequence to targets."""
686
687 def gather(key, dist='b', targets='all', block=True):
688 """Gather object key from targets"""
689
690 def raw_map(func, seqs, dist='b', targets='all', block=True):
691 """
692 A parallelized version of Python's builtin map.
693
694 This has a slightly different syntax than the builtin `map`.
695 This is needed because we need to have keyword arguments and thus
696 can't use *args to capture all the sequences. Instead, they must
697 be passed in a list or tuple.
698
699 raw_map(func, seqs) -> map(func, seqs[0], seqs[1], ...)
700
701 Most users will want to use parallel functions or the `mapper`
702 and `map` methods for an API that follows that of the builtin
703 `map`.
704 """
717
705
718
706
719 #-------------------------------------------------------------------------------
707 #-------------------------------------------------------------------------------
@@ -722,46 +710,31 b' class ISynchronousMultiEngineCoordinator(IMultiEngineCoordinator):'
722
710
723 class IMultiEngineExtras(Interface):
711 class IMultiEngineExtras(Interface):
724
712
725 def zip_pull(targets, *keys):
713 def zip_pull(targets, keys):
726 """Pull, but return results in a different format from `pull`.
714 """
715 Pull, but return results in a different format from `pull`.
727
716
728 This method basically returns zip(pull(targets, *keys)), with a few
717 This method basically returns zip(pull(targets, *keys)), with a few
729 edge cases handled differently. Users of chainsaw will find this format
718 edge cases handled differently. Users of chainsaw will find this format
730 familiar.
719 familiar.
731
732 :Parameters:
733 targets : int, list or 'all'
734 The engine ids the action will apply to. Call `get_ids` to see
735 a list of currently available engines.
736 keys: list or tuple of str
737 A list of variable names as string of the Python objects to be pulled
738 back to the client.
739
740 :Returns: A list of pulled Python objects for each target.
741 """
720 """
742
721
743 def run(targets, fname):
722 def run(targets, fname):
744 """Run a .py file on targets.
723 """Run a .py file on targets."""
745
746 :Parameters:
747 targets : int, list or 'all'
748 The engine ids the action will apply to. Call `get_ids` to see
749 a list of currently available engines.
750 fname : str
751 The filename of a .py file on the local system to be sent to and run
752 on the engines.
753 block : boolean
754 Should I block or not. If block=True, wait for the action to
755 complete and return the result. If block=False, return a
756 `PendingResult` object that can be used to later get the
757 result. If block is not specified, the block attribute
758 will be used instead.
759 """
760
724
761
725
762 class ISynchronousMultiEngineExtras(IMultiEngineExtras):
726 class ISynchronousMultiEngineExtras(IMultiEngineExtras):
763 pass
727 def zip_pull(targets, keys, block=True):
764
728 """
729 Pull, but return results in a different format from `pull`.
730
731 This method basically returns zip(pull(targets, *keys)), with a few
732 edge cases handled differently. Users of chainsaw will find this format
733 familiar.
734 """
735
736 def run(targets, fname, block=True):
737 """Run a .py file on targets."""
765
738
766 #-------------------------------------------------------------------------------
739 #-------------------------------------------------------------------------------
767 # The full MultiEngine interface
740 # The full MultiEngine interface
@@ -31,6 +31,11 b' from IPython.ColorANSI import TermColors'
31 from IPython.kernel.twistedutil import blockingCallFromThread
31 from IPython.kernel.twistedutil import blockingCallFromThread
32 from IPython.kernel import error
32 from IPython.kernel import error
33 from IPython.kernel.parallelfunction import ParallelFunction
33 from IPython.kernel.parallelfunction import ParallelFunction
34 from IPython.kernel.mapper import (
35 MultiEngineMapper,
36 IMultiEngineMapperFactory,
37 IMapper
38 )
34 from IPython.kernel import map as Map
39 from IPython.kernel import map as Map
35 from IPython.kernel import multiengine as me
40 from IPython.kernel import multiengine as me
36 from IPython.kernel.multiengine import (IFullMultiEngine,
41 from IPython.kernel.multiengine import (IFullMultiEngine,
@@ -186,10 +191,14 b' class ResultList(list):'
186
191
187 def __repr__(self):
192 def __repr__(self):
188 output = []
193 output = []
189 blue = TermColors.Blue
194 # These colored prompts were not working on Windows
190 normal = TermColors.Normal
195 if sys.platform == 'win32':
191 red = TermColors.Red
196 blue = normal = red = green = ''
192 green = TermColors.Green
197 else:
198 blue = TermColors.Blue
199 normal = TermColors.Normal
200 red = TermColors.Red
201 green = TermColors.Green
193 output.append("<Results List>\n")
202 output.append("<Results List>\n")
194 for cmd in self:
203 for cmd in self:
195 if isinstance(cmd, Failure):
204 if isinstance(cmd, Failure):
@@ -294,35 +303,7 b' class InteractiveMultiEngineClient(object):'
294 def __len__(self):
303 def __len__(self):
295 """Return the number of available engines."""
304 """Return the number of available engines."""
296 return len(self.get_ids())
305 return len(self.get_ids())
297
306
298 def parallelize(self, func, targets=None, block=None):
299 """Build a `ParallelFunction` object for functionName on engines.
300
301 The returned object will implement a parallel version of functionName
302 that takes a local sequence as its only argument and calls (in
303 parallel) functionName on each element of that sequence. The
304 `ParallelFunction` object has a `targets` attribute that controls
305 which engines the function is run on.
306
307 :Parameters:
308 targets : int, list or 'all'
309 The engine ids the action will apply to. Call `get_ids` to see
310 a list of currently available engines.
311 functionName : str
312 A Python string that names a callable defined on the engines.
313
314 :Returns: A `ParallelFunction` object.
315
316 Examples
317 ========
318
319 >>> psin = rc.parallelize('all','lambda x:sin(x)')
320 >>> psin(range(10000))
321 [0,2,4,9,25,36,...]
322 """
323 targets, block = self._findTargetsAndBlock(targets, block)
324 return ParallelFunction(func, self, targets, block)
325
326 #---------------------------------------------------------------------------
307 #---------------------------------------------------------------------------
327 # Make this a context manager for with
308 # Make this a context manager for with
328 #---------------------------------------------------------------------------
309 #---------------------------------------------------------------------------
@@ -422,7 +403,11 b' class FullBlockingMultiEngineClient(InteractiveMultiEngineClient):'
422 engine, run code on it, etc.
403 engine, run code on it, etc.
423 """
404 """
424
405
425 implements(IFullBlockingMultiEngineClient)
406 implements(
407 IFullBlockingMultiEngineClient,
408 IMultiEngineMapperFactory,
409 IMapper
410 )
426
411
427 def __init__(self, smultiengine):
412 def __init__(self, smultiengine):
428 self.smultiengine = smultiengine
413 self.smultiengine = smultiengine
@@ -779,29 +764,100 b' class FullBlockingMultiEngineClient(InteractiveMultiEngineClient):'
779 # IMultiEngineCoordinator
764 # IMultiEngineCoordinator
780 #---------------------------------------------------------------------------
765 #---------------------------------------------------------------------------
781
766
782 def scatter(self, key, seq, style='basic', flatten=False, targets=None, block=None):
767 def scatter(self, key, seq, dist='b', flatten=False, targets=None, block=None):
783 """
768 """
784 Partition a Python sequence and send the partitions to a set of engines.
769 Partition a Python sequence and send the partitions to a set of engines.
785 """
770 """
786 targets, block = self._findTargetsAndBlock(targets, block)
771 targets, block = self._findTargetsAndBlock(targets, block)
787 return self._blockFromThread(self.smultiengine.scatter, key, seq,
772 return self._blockFromThread(self.smultiengine.scatter, key, seq,
788 style, flatten, targets=targets, block=block)
773 dist, flatten, targets=targets, block=block)
789
774
790 def gather(self, key, style='basic', targets=None, block=None):
775 def gather(self, key, dist='b', targets=None, block=None):
791 """
776 """
792 Gather a partitioned sequence on a set of engines as a single local seq.
777 Gather a partitioned sequence on a set of engines as a single local seq.
793 """
778 """
794 targets, block = self._findTargetsAndBlock(targets, block)
779 targets, block = self._findTargetsAndBlock(targets, block)
795 return self._blockFromThread(self.smultiengine.gather, key, style,
780 return self._blockFromThread(self.smultiengine.gather, key, dist,
796 targets=targets, block=block)
781 targets=targets, block=block)
797
782
798 def map(self, func, seq, style='basic', targets=None, block=None):
783 def raw_map(self, func, seq, dist='b', targets=None, block=None):
799 """
784 """
800 A parallelized version of Python's builtin map
785 A parallelized version of Python's builtin map.
786
787 This has a slightly different syntax than the builtin `map`.
788 This is needed because we need to have keyword arguments and thus
789 can't use *args to capture all the sequences. Instead, they must
790 be passed in a list or tuple.
791
792 raw_map(func, seqs) -> map(func, seqs[0], seqs[1], ...)
793
794 Most users will want to use parallel functions or the `mapper`
795 and `map` methods for an API that follows that of the builtin
796 `map`.
801 """
797 """
802 targets, block = self._findTargetsAndBlock(targets, block)
798 targets, block = self._findTargetsAndBlock(targets, block)
803 return self._blockFromThread(self.smultiengine.map, func, seq,
799 return self._blockFromThread(self.smultiengine.raw_map, func, seq,
804 style, targets=targets, block=block)
800 dist, targets=targets, block=block)
801
802 def map(self, func, *sequences):
803 """
804 A parallel version of Python's builtin `map` function.
805
806 This method applies a function to sequences of arguments. It
807 follows the same syntax as the builtin `map`.
808
809 This method creates a mapper objects by calling `self.mapper` with
810 no arguments and then uses that mapper to do the mapping. See
811 the documentation of `mapper` for more details.
812 """
813 return self.mapper().map(func, *sequences)
814
815 def mapper(self, dist='b', targets='all', block=None):
816 """
817 Create a mapper object that has a `map` method.
818
819 This method returns an object that implements the `IMapper`
820 interface. This method is a factory that is used to control how
821 the map happens.
822
823 :Parameters:
824 dist : str
825 What decomposition to use, 'b' is the only one supported
826 currently
827 targets : str, int, sequence of ints
828 Which engines to use for the map
829 block : boolean
830 Should calls to `map` block or not
831 """
832 return MultiEngineMapper(self, dist, targets, block)
833
834 def parallel(self, dist='b', targets=None, block=None):
835 """
836 A decorator that turns a function into a parallel function.
837
838 This can be used as:
839
840 @parallel()
841 def f(x, y)
842 ...
843
844 f(range(10), range(10))
845
846 This causes f(0,0), f(1,1), ... to be called in parallel.
847
848 :Parameters:
849 dist : str
850 What decomposition to use, 'b' is the only one supported
851 currently
852 targets : str, int, sequence of ints
853 Which engines to use for the map
854 block : boolean
855 Should calls to `map` block or not
856 """
857 targets, block = self._findTargetsAndBlock(targets, block)
858 mapper = self.mapper(dist, targets, block)
859 pf = ParallelFunction(mapper)
860 return pf
805
861
806 #---------------------------------------------------------------------------
862 #---------------------------------------------------------------------------
807 # IMultiEngineExtras
863 # IMultiEngineExtras
@@ -29,6 +29,12 b' from foolscap import Referenceable'
29 from IPython.kernel import error
29 from IPython.kernel import error
30 from IPython.kernel.util import printer
30 from IPython.kernel.util import printer
31 from IPython.kernel import map as Map
31 from IPython.kernel import map as Map
32 from IPython.kernel.parallelfunction import ParallelFunction
33 from IPython.kernel.mapper import (
34 MultiEngineMapper,
35 IMultiEngineMapperFactory,
36 IMapper
37 )
32 from IPython.kernel.twistedutil import gatherBoth
38 from IPython.kernel.twistedutil import gatherBoth
33 from IPython.kernel.multiengine import (MultiEngine,
39 from IPython.kernel.multiengine import (MultiEngine,
34 IMultiEngine,
40 IMultiEngine,
@@ -280,7 +286,12 b' components.registerAdapter(FCSynchronousMultiEngineFromMultiEngine,'
280
286
281 class FCFullSynchronousMultiEngineClient(object):
287 class FCFullSynchronousMultiEngineClient(object):
282
288
283 implements(IFullSynchronousMultiEngine, IBlockingClientAdaptor)
289 implements(
290 IFullSynchronousMultiEngine,
291 IBlockingClientAdaptor,
292 IMultiEngineMapperFactory,
293 IMapper
294 )
284
295
285 def __init__(self, remote_reference):
296 def __init__(self, remote_reference):
286 self.remote_reference = remote_reference
297 self.remote_reference = remote_reference
@@ -475,7 +486,7 b' class FCFullSynchronousMultiEngineClient(object):'
475 d.addCallback(create_targets)
486 d.addCallback(create_targets)
476 return d
487 return d
477
488
478 def scatter(self, key, seq, style='basic', flatten=False, targets='all', block=True):
489 def scatter(self, key, seq, dist='b', flatten=False, targets='all', block=True):
479
490
480 # Note: scatter and gather handle pending deferreds locally through self.pdm.
491 # Note: scatter and gather handle pending deferreds locally through self.pdm.
481 # This enables us to collect a bunch fo deferred ids and make a secondary
492 # This enables us to collect a bunch fo deferred ids and make a secondary
@@ -483,7 +494,7 b' class FCFullSynchronousMultiEngineClient(object):'
483 # difficult to get right though.
494 # difficult to get right though.
484 def do_scatter(engines):
495 def do_scatter(engines):
485 nEngines = len(engines)
496 nEngines = len(engines)
486 mapClass = Map.styles[style]
497 mapClass = Map.dists[dist]
487 mapObject = mapClass()
498 mapObject = mapClass()
488 d_list = []
499 d_list = []
489 # Loop through and push to each engine in non-blocking mode.
500 # Loop through and push to each engine in non-blocking mode.
@@ -541,7 +552,7 b' class FCFullSynchronousMultiEngineClient(object):'
541 d.addCallback(do_scatter)
552 d.addCallback(do_scatter)
542 return d
553 return d
543
554
544 def gather(self, key, style='basic', targets='all', block=True):
555 def gather(self, key, dist='b', targets='all', block=True):
545
556
546 # Note: scatter and gather handle pending deferreds locally through self.pdm.
557 # Note: scatter and gather handle pending deferreds locally through self.pdm.
547 # This enables us to collect a bunch fo deferred ids and make a secondary
558 # This enables us to collect a bunch fo deferred ids and make a secondary
@@ -549,7 +560,7 b' class FCFullSynchronousMultiEngineClient(object):'
549 # difficult to get right though.
560 # difficult to get right though.
550 def do_gather(engines):
561 def do_gather(engines):
551 nEngines = len(engines)
562 nEngines = len(engines)
552 mapClass = Map.styles[style]
563 mapClass = Map.dists[dist]
553 mapObject = mapClass()
564 mapObject = mapClass()
554 d_list = []
565 d_list = []
555 # Loop through and push to each engine in non-blocking mode.
566 # Loop through and push to each engine in non-blocking mode.
@@ -604,25 +615,103 b' class FCFullSynchronousMultiEngineClient(object):'
604 d.addCallback(do_gather)
615 d.addCallback(do_gather)
605 return d
616 return d
606
617
607 def map(self, func, seq, style='basic', targets='all', block=True):
618 def raw_map(self, func, sequences, dist='b', targets='all', block=True):
608 d_list = []
619 """
620 A parallelized version of Python's builtin map.
621
622 This has a slightly different syntax than the builtin `map`.
623 This is needed because we need to have keyword arguments and thus
624 can't use *args to capture all the sequences. Instead, they must
625 be passed in a list or tuple.
626
627 raw_map(func, seqs) -> map(func, seqs[0], seqs[1], ...)
628
629 Most users will want to use parallel functions or the `mapper`
630 and `map` methods for an API that follows that of the builtin
631 `map`.
632 """
633 if not isinstance(sequences, (list, tuple)):
634 raise TypeError('sequences must be a list or tuple')
635 max_len = max(len(s) for s in sequences)
636 for s in sequences:
637 if len(s)!=max_len:
638 raise ValueError('all sequences must have equal length')
609 if isinstance(func, FunctionType):
639 if isinstance(func, FunctionType):
610 d = self.push_function(dict(_ipython_map_func=func), targets=targets, block=False)
640 d = self.push_function(dict(_ipython_map_func=func), targets=targets, block=False)
611 d.addCallback(lambda did: self.get_pending_deferred(did, True))
641 d.addCallback(lambda did: self.get_pending_deferred(did, True))
612 sourceToRun = '_ipython_map_seq_result = map(_ipython_map_func, _ipython_map_seq)'
642 sourceToRun = '_ipython_map_seq_result = map(_ipython_map_func, *zip(*_ipython_map_seq))'
613 elif isinstance(func, str):
643 elif isinstance(func, str):
614 d = defer.succeed(None)
644 d = defer.succeed(None)
615 sourceToRun = \
645 sourceToRun = \
616 '_ipython_map_seq_result = map(%s, _ipython_map_seq)' % func
646 '_ipython_map_seq_result = map(%s, *zip(*_ipython_map_seq))' % func
617 else:
647 else:
618 raise TypeError("func must be a function or str")
648 raise TypeError("func must be a function or str")
619
649
620 d.addCallback(lambda _: self.scatter('_ipython_map_seq', seq, style, targets=targets))
650 d.addCallback(lambda _: self.scatter('_ipython_map_seq', zip(*sequences), dist, targets=targets))
621 d.addCallback(lambda _: self.execute(sourceToRun, targets=targets, block=False))
651 d.addCallback(lambda _: self.execute(sourceToRun, targets=targets, block=False))
622 d.addCallback(lambda did: self.get_pending_deferred(did, True))
652 d.addCallback(lambda did: self.get_pending_deferred(did, True))
623 d.addCallback(lambda _: self.gather('_ipython_map_seq_result', style, targets=targets, block=block))
653 d.addCallback(lambda _: self.gather('_ipython_map_seq_result', dist, targets=targets, block=block))
624 return d
654 return d
625
655
656 def map(self, func, *sequences):
657 """
658 A parallel version of Python's builtin `map` function.
659
660 This method applies a function to sequences of arguments. It
661 follows the same syntax as the builtin `map`.
662
663 This method creates a mapper objects by calling `self.mapper` with
664 no arguments and then uses that mapper to do the mapping. See
665 the documentation of `mapper` for more details.
666 """
667 return self.mapper().map(func, *sequences)
668
669 def mapper(self, dist='b', targets='all', block=True):
670 """
671 Create a mapper object that has a `map` method.
672
673 This method returns an object that implements the `IMapper`
674 interface. This method is a factory that is used to control how
675 the map happens.
676
677 :Parameters:
678 dist : str
679 What decomposition to use, 'b' is the only one supported
680 currently
681 targets : str, int, sequence of ints
682 Which engines to use for the map
683 block : boolean
684 Should calls to `map` block or not
685 """
686 return MultiEngineMapper(self, dist, targets, block)
687
688 def parallel(self, dist='b', targets='all', block=True):
689 """
690 A decorator that turns a function into a parallel function.
691
692 This can be used as:
693
694 @parallel()
695 def f(x, y)
696 ...
697
698 f(range(10), range(10))
699
700 This causes f(0,0), f(1,1), ... to be called in parallel.
701
702 :Parameters:
703 dist : str
704 What decomposition to use, 'b' is the only one supported
705 currently
706 targets : str, int, sequence of ints
707 Which engines to use for the map
708 block : boolean
709 Should calls to `map` block or not
710 """
711 mapper = self.mapper(dist, targets, block)
712 pf = ParallelFunction(mapper)
713 return pf
714
626 #---------------------------------------------------------------------------
715 #---------------------------------------------------------------------------
627 # ISynchronousMultiEngineExtras related methods
716 # ISynchronousMultiEngineExtras related methods
628 #---------------------------------------------------------------------------
717 #---------------------------------------------------------------------------
@@ -16,17 +16,92 b' __docformat__ = "restructuredtext en"'
16 #-------------------------------------------------------------------------------
16 #-------------------------------------------------------------------------------
17
17
18 from types import FunctionType
18 from types import FunctionType
19 from zope.interface import Interface, implements
19
20
20 class ParallelFunction:
21
21 """A function that operates in parallel on sequences."""
22 class IMultiEngineParallelDecorator(Interface):
22 def __init__(self, func, multiengine, targets, block):
23 """A decorator that creates a parallel function."""
23 """Create a `ParallelFunction`.
24
25 def parallel(dist='b', targets=None, block=None):
26 """
27 A decorator that turns a function into a parallel function.
28
29 This can be used as:
30
31 @parallel()
32 def f(x, y)
33 ...
34
35 f(range(10), range(10))
36
37 This causes f(0,0), f(1,1), ... to be called in parallel.
38
39 :Parameters:
40 dist : str
41 What decomposition to use, 'b' is the only one supported
42 currently
43 targets : str, int, sequence of ints
44 Which engines to use for the map
45 block : boolean
46 Should calls to `map` block or not
47 """
48
49 class ITaskParallelDecorator(Interface):
50 """A decorator that creates a parallel function."""
51
52 def parallel(clear_before=False, clear_after=False, retries=0,
53 recovery_task=None, depend=None, block=True):
54 """
55 A decorator that turns a function into a parallel function.
56
57 This can be used as:
58
59 @parallel()
60 def f(x, y)
61 ...
62
63 f(range(10), range(10))
64
65 This causes f(0,0), f(1,1), ... to be called in parallel.
66
67 See the documentation for `IPython.kernel.task.BaseTask` for
68 documentation on the arguments to this method.
69 """
70
71 class IParallelFunction(Interface):
72 pass
73
74 class ParallelFunction(object):
75 """
76 The implementation of a parallel function.
77
78 A parallel function is similar to Python's map function:
79
80 map(func, *sequences) -> pfunc(*sequences)
81
82 Parallel functions should be created by using the @parallel decorator.
83 """
84
85 implements(IParallelFunction)
86
87 def __init__(self, mapper):
88 """
89 Create a parallel function from an `IMapper`.
90
91 :Parameters:
92 mapper : an `IMapper` implementer.
93 The mapper to use for the parallel function
94 """
95 self.mapper = mapper
96
97 def __call__(self, func):
98 """
99 Decorate a function to make it run in parallel.
24 """
100 """
25 assert isinstance(func, (str, FunctionType)), "func must be a fuction or str"
101 assert isinstance(func, (str, FunctionType)), "func must be a fuction or str"
26 self.func = func
102 self.func = func
27 self.multiengine = multiengine
103 def call_function(*sequences):
28 self.targets = targets
104 return self.mapper.map(self.func, *sequences)
29 self.block = block
105 return call_function
30
106
31 def __call__(self, sequence):
107 No newline at end of file
32 return self.multiengine.map(self.func, sequence, targets=self.targets, block=self.block) No newline at end of file
This diff has been collapsed as it changes many lines, (680 lines changed) Show them Hide them
@@ -5,116 +5,404 b''
5
5
6 __docformat__ = "restructuredtext en"
6 __docformat__ = "restructuredtext en"
7
7
8 #-------------------------------------------------------------------------------
8 #-----------------------------------------------------------------------------
9 # Copyright (C) 2008 The IPython Development Team
9 # Copyright (C) 2008 The IPython Development Team
10 #
10 #
11 # Distributed under the terms of the BSD License. The full license is in
11 # Distributed under the terms of the BSD License. The full license is in
12 # the file COPYING, distributed as part of this software.
12 # the file COPYING, distributed as part of this software.
13 #-------------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14
14
15 #-------------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 # Imports
16 # Imports
17 #-------------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18
18
19 import copy, time
19 import copy, time
20 from types import FunctionType as function
20 from types import FunctionType
21
21
22 import zope.interface as zi, string
22 import zope.interface as zi, string
23 from twisted.internet import defer, reactor
23 from twisted.internet import defer, reactor
24 from twisted.python import components, log, failure
24 from twisted.python import components, log, failure
25
25
26 # from IPython.genutils import time
26 from IPython.kernel.util import printer
27
28 from IPython.kernel import engineservice as es, error
27 from IPython.kernel import engineservice as es, error
29 from IPython.kernel import controllerservice as cs
28 from IPython.kernel import controllerservice as cs
30 from IPython.kernel.twistedutil import gatherBoth, DeferredList
29 from IPython.kernel.twistedutil import gatherBoth, DeferredList
31
30
32 from IPython.kernel.pickleutil import can,uncan, CannedFunction
31 from IPython.kernel.pickleutil import can, uncan, CannedFunction
33
34 def canTask(task):
35 t = copy.copy(task)
36 t.depend = can(t.depend)
37 if t.recovery_task:
38 t.recovery_task = canTask(t.recovery_task)
39 return t
40
32
41 def uncanTask(task):
33 #-----------------------------------------------------------------------------
42 t = copy.copy(task)
34 # Definition of the Task objects
43 t.depend = uncan(t.depend)
35 #-----------------------------------------------------------------------------
44 if t.recovery_task and t.recovery_task is not task:
45 t.recovery_task = uncanTask(t.recovery_task)
46 return t
47
36
48 time_format = '%Y/%m/%d %H:%M:%S'
37 time_format = '%Y/%m/%d %H:%M:%S'
49
38
50 class Task(object):
39 class ITask(zi.Interface):
51 """Our representation of a task for the `TaskController` interface.
40 """
52
41 This interface provides a generic definition of what constitutes a task.
53 The user should create instances of this class to represent a task that
42
54 needs to be done.
43 There are two sides to a task. First a task needs to take input from
55
44 a user to determine what work is performed by the task. Second, the
56 :Parameters:
45 task needs to have the logic that knows how to turn that information
57 expression : str
46 info specific calls to a worker, through the `IQueuedEngine` interface.
58 A str that is valid python code that is the task.
59 pull : str or list of str
60 The names of objects to be pulled as results. If not specified,
61 will return {'result', None}
62 push : dict
63 A dict of objects to be pushed into the engines namespace before
64 execution of the expression.
65 clear_before : boolean
66 Should the engine's namespace be cleared before the task is run.
67 Default=False.
68 clear_after : boolean
69 Should the engine's namespace be cleared after the task is run.
70 Default=False.
71 retries : int
72 The number of times to resumbit the task if it fails. Default=0.
73 recovery_task : Task
74 This is the Task to be run when the task has exhausted its retries
75 Default=None.
76 depend : bool function(properties)
77 This is the dependency function for the Task, which determines
78 whether a task can be run on a Worker. `depend` is called with
79 one argument, the worker's properties dict, and should return
80 True if the worker meets the dependencies or False if it does
81 not.
82 Default=None - run on any worker
83 options : dict
84 Any other keyword options for more elaborate uses of tasks
85
86 Examples
87 --------
88
47
89 >>> t = Task('dostuff(args)')
48 Many method in this class get two things passed to them: a Deferred
90 >>> t = Task('a=5', pull='a')
49 and an IQueuedEngine implementer. Such methods should register callbacks
91 >>> t = Task('a=5\nb=4', pull=['a','b'])
50 on the Deferred that use the IQueuedEngine to accomplish something. See
92 >>> t = Task('os.kill(os.getpid(),9)', retries=100) # this is a bad idea
51 the existing task objects for examples.
93 # A dependency case:
94 >>> def hasMPI(props):
95 ... return props.get('mpi') is not None
96 >>> t = Task('mpi.send(blah,blah)', depend = hasMPI)
97 """
52 """
98
53
99 def __init__(self, expression, pull=None, push=None,
54 zi.Attribute('retries','How many times to retry the task')
100 clear_before=False, clear_after=False, retries=0,
55 zi.Attribute('recovery_task','A task to try if the initial one fails')
101 recovery_task=None, depend=None, **options):
56 zi.Attribute('taskid','the id of the task')
102 self.expression = expression
57
103 if isinstance(pull, str):
58 def start_time(result):
104 self.pull = [pull]
59 """
105 else:
60 Do anything needed to start the timing of the task.
106 self.pull = pull
61
107 self.push = push
62 Must simply return the result after starting the timers.
63 """
64
65 def stop_time(result):
66 """
67 Do anything needed to stop the timing of the task.
68
69 Must simply return the result after stopping the timers. This
70 method will usually set attributes that are used by `process_result`
71 in building result of the task.
72 """
73
74 def pre_task(d, queued_engine):
75 """Do something with the queued_engine before the task is run.
76
77 This method should simply add callbacks to the input Deferred
78 that do something with the `queued_engine` before the task is run.
79
80 :Parameters:
81 d : Deferred
82 The deferred that actions should be attached to
83 queued_engine : IQueuedEngine implementer
84 The worker that has been allocated to perform the task
85 """
86
87 def post_task(d, queued_engine):
88 """Do something with the queued_engine after the task is run.
89
90 This method should simply add callbacks to the input Deferred
91 that do something with the `queued_engine` before the task is run.
92
93 :Parameters:
94 d : Deferred
95 The deferred that actions should be attached to
96 queued_engine : IQueuedEngine implementer
97 The worker that has been allocated to perform the task
98 """
99
100 def submit_task(d, queued_engine):
101 """Submit a task using the `queued_engine` we have been allocated.
102
103 When a task is ready to run, this method is called. This method
104 must take the internal information of the task and make suitable
105 calls on the queued_engine to have the actual work done.
106
107 This method should simply add callbacks to the input Deferred
108 that do something with the `queued_engine` before the task is run.
109
110 :Parameters:
111 d : Deferred
112 The deferred that actions should be attached to
113 queued_engine : IQueuedEngine implementer
114 The worker that has been allocated to perform the task
115 """
116
117 def process_result(d, result, engine_id):
118 """Take a raw task result.
119
120 Objects that implement `ITask` can choose how the result of running
121 the task is presented. This method takes the raw result and
122 does this logic. Two example are the `MapTask` which simply returns
123 the raw result or a `Failure` object and the `StringTask` which
124 returns a `TaskResult` object.
125
126 :Parameters:
127 d : Deferred
128 The deferred that actions should be attached to
129 result : object
130 The raw task result that needs to be wrapped
131 engine_id : int
132 The id of the engine that did the task
133
134 :Returns:
135 The result, as a tuple of the form: (success, result).
136 Here, success is a boolean indicating if the task
137 succeeded or failed and result is the result.
138 """
139
140 def check_depend(properties):
141 """Check properties to see if the task should be run.
142
143 :Parameters:
144 properties : dict
145 A dictionary of properties that an engine has set
146
147 :Returns:
148 True if the task should be run, False otherwise
149 """
150
151 def can_task(self):
152 """Serialize (can) any functions in the task for pickling.
153
154 Subclasses must override this method and make sure that all
155 functions in the task are canned by calling `can` on the
156 function.
157 """
158
159 def uncan_task(self):
160 """Unserialize (uncan) any canned function in the task."""
161
162 class BaseTask(object):
163 """
164 Common fuctionality for all objects implementing `ITask`.
165 """
166
167 zi.implements(ITask)
168
169 def __init__(self, clear_before=False, clear_after=False, retries=0,
170 recovery_task=None, depend=None):
171 """
172 Make a generic task.
173
174 :Parameters:
175 clear_before : boolean
176 Should the engines namespace be cleared before the task
177 is run
178 clear_after : boolean
179 Should the engines namespace be clear after the task is run
180 retries : int
181 The number of times a task should be retries upon failure
182 recovery_task : any task object
183 If a task fails and it has a recovery_task, that is run
184 upon a retry
185 depend : FunctionType
186 A function that is called to test for properties. This function
187 must take one argument, the properties dict and return a boolean
188 """
108 self.clear_before = clear_before
189 self.clear_before = clear_before
109 self.clear_after = clear_after
190 self.clear_after = clear_after
110 self.retries=retries
191 self.retries = retries
111 self.recovery_task = recovery_task
192 self.recovery_task = recovery_task
112 self.depend = depend
193 self.depend = depend
113 self.options = options
114 self.taskid = None
194 self.taskid = None
195
196 def start_time(self, result):
197 """
198 Start the basic timers.
199 """
200 self.start = time.time()
201 self.start_struct = time.localtime()
202 return result
203
204 def stop_time(self, result):
205 """
206 Stop the basic timers.
207 """
208 self.stop = time.time()
209 self.stop_struct = time.localtime()
210 self.duration = self.stop - self.start
211 self.submitted = time.strftime(time_format, self.start_struct)
212 self.completed = time.strftime(time_format)
213 return result
214
215 def pre_task(self, d, queued_engine):
216 """
217 Clear the engine before running the task if clear_before is set.
218 """
219 if self.clear_before:
220 d.addCallback(lambda r: queued_engine.reset())
221
222 def post_task(self, d, queued_engine):
223 """
224 Clear the engine after running the task if clear_after is set.
225 """
226 def reseter(result):
227 queued_engine.reset()
228 return result
229 if self.clear_after:
230 d.addBoth(reseter)
231
232 def submit_task(self, d, queued_engine):
233 raise NotImplementedError('submit_task must be implemented in a subclass')
234
235 def process_result(self, result, engine_id):
236 """
237 Process a task result.
238
239 This is the default `process_result` that just returns the raw
240 result or a `Failure`.
241 """
242 if isinstance(result, failure.Failure):
243 return (False, result)
244 else:
245 return (True, result)
246
247 def check_depend(self, properties):
248 """
249 Calls self.depend(properties) to see if a task should be run.
250 """
251 if self.depend is not None:
252 return self.depend(properties)
253 else:
254 return True
255
256 def can_task(self):
257 self.depend = can(self.depend)
258 if isinstance(self.recovery_task, BaseTask):
259 self.recovery_task.can_task()
260
261 def uncan_task(self):
262 self.depend = uncan(self.depend)
263 if isinstance(self.recovery_task, BaseTask):
264 self.recovery_task.uncan_task()
265
266 class MapTask(BaseTask):
267 """
268 A task that consists of a function and arguments.
269 """
270
271 zi.implements(ITask)
272
273 def __init__(self, function, args=None, kwargs=None, clear_before=False,
274 clear_after=False, retries=0, recovery_task=None, depend=None):
275 """
276 Create a task based on a function, args and kwargs.
277
278 This is a simple type of task that consists of calling:
279 function(*args, **kwargs) and wrapping the result in a `TaskResult`.
280
281 The return value of the function, or a `Failure` wrapping an
282 exception is the task result for this type of task.
283 """
284 BaseTask.__init__(self, clear_before, clear_after, retries,
285 recovery_task, depend)
286 if not isinstance(function, FunctionType):
287 raise TypeError('a task function must be a FunctionType')
288 self.function = function
289 if args is None:
290 self.args = ()
291 else:
292 self.args = args
293 if not isinstance(self.args, (list, tuple)):
294 raise TypeError('a task args must be a list or tuple')
295 if kwargs is None:
296 self.kwargs = {}
297 else:
298 self.kwargs = kwargs
299 if not isinstance(self.kwargs, dict):
300 raise TypeError('a task kwargs must be a dict')
301
302 def submit_task(self, d, queued_engine):
303 d.addCallback(lambda r: queued_engine.push_function(
304 dict(_ipython_task_function=self.function))
305 )
306 d.addCallback(lambda r: queued_engine.push(
307 dict(_ipython_task_args=self.args,_ipython_task_kwargs=self.kwargs))
308 )
309 d.addCallback(lambda r: queued_engine.execute(
310 '_ipython_task_result = _ipython_task_function(*_ipython_task_args,**_ipython_task_kwargs)')
311 )
312 d.addCallback(lambda r: queued_engine.pull('_ipython_task_result'))
313
314 def can_task(self):
315 self.function = can(self.function)
316 BaseTask.can_task(self)
317
318 def uncan_task(self):
319 self.function = uncan(self.function)
320 BaseTask.uncan_task(self)
321
322
323 class StringTask(BaseTask):
324 """
325 A task that consists of a string of Python code to run.
326 """
327
328 def __init__(self, expression, pull=None, push=None,
329 clear_before=False, clear_after=False, retries=0,
330 recovery_task=None, depend=None):
331 """
332 Create a task based on a Python expression and variables
333
334 This type of task lets you push a set of variables to the engines
335 namespace, run a Python string in that namespace and then bring back
336 a different set of Python variables as the result.
337
338 Because this type of task can return many results (through the
339 `pull` keyword argument) it returns a special `TaskResult` object
340 that wraps the pulled variables, statistics about the run and
341 any exceptions raised.
342 """
343 if not isinstance(expression, str):
344 raise TypeError('a task expression must be a string')
345 self.expression = expression
346
347 if pull==None:
348 self.pull = ()
349 elif isinstance(pull, str):
350 self.pull = (pull,)
351 elif isinstance(pull, (list, tuple)):
352 self.pull = pull
353 else:
354 raise TypeError('pull must be str or a sequence of strs')
355
356 if push==None:
357 self.push = {}
358 elif isinstance(push, dict):
359 self.push = push
360 else:
361 raise TypeError('push must be a dict')
362
363 BaseTask.__init__(self, clear_before, clear_after, retries,
364 recovery_task, depend)
365
366 def submit_task(self, d, queued_engine):
367 if self.push is not None:
368 d.addCallback(lambda r: queued_engine.push(self.push))
369
370 d.addCallback(lambda r: queued_engine.execute(self.expression))
371
372 if self.pull is not None:
373 d.addCallback(lambda r: queued_engine.pull(self.pull))
374 else:
375 d.addCallback(lambda r: None)
376
377 def process_result(self, result, engine_id):
378 if isinstance(result, failure.Failure):
379 tr = TaskResult(result, engine_id)
380 else:
381 if self.pull is None:
382 resultDict = {}
383 elif len(self.pull) == 1:
384 resultDict = {self.pull[0]:result}
385 else:
386 resultDict = dict(zip(self.pull, result))
387 tr = TaskResult(resultDict, engine_id)
388 # Assign task attributes
389 tr.submitted = self.submitted
390 tr.completed = self.completed
391 tr.duration = self.duration
392 if hasattr(self,'taskid'):
393 tr.taskid = self.taskid
394 else:
395 tr.taskid = None
396 if isinstance(result, failure.Failure):
397 return (False, tr)
398 else:
399 return (True, tr)
115
400
116 class ResultNS:
401 class ResultNS(object):
117 """The result namespace object for use in TaskResult objects as tr.ns.
402 """
403 A dict like object for holding the results of a task.
404
405 The result namespace object for use in `TaskResult` objects as tr.ns.
118 It builds an object from a dictionary, such that it has attributes
406 It builds an object from a dictionary, such that it has attributes
119 according to the key,value pairs of the dictionary.
407 according to the key,value pairs of the dictionary.
120
408
@@ -128,11 +416,11 b' class ResultNS:'
128
416
129 >>> ns = ResultNS({'a':17,'foo':range(3)})
417 >>> ns = ResultNS({'a':17,'foo':range(3)})
130 >>> print ns
418 >>> print ns
131 NS{'a':17,'foo':range(3)}
419 NS{'a': 17, 'foo': [0, 1, 2]}
132 >>> ns.a
420 >>> ns.a
133 17
421 17
134 >>> ns['foo']
422 >>> ns['foo']
135 [0,1,2]
423 [0, 1, 2]
136 """
424 """
137 def __init__(self, dikt):
425 def __init__(self, dikt):
138 for k,v in dikt.iteritems():
426 for k,v in dikt.iteritems():
@@ -152,7 +440,7 b' class ResultNS:'
152
440
153 class TaskResult(object):
441 class TaskResult(object):
154 """
442 """
155 An object for returning task results.
443 An object for returning task results for certain types of tasks.
156
444
157 This object encapsulates the results of a task. On task
445 This object encapsulates the results of a task. On task
158 success it will have a keys attribute that will have a list
446 success it will have a keys attribute that will have a list
@@ -162,21 +450,21 b' class TaskResult(object):'
162
450
163 In task failure, keys will be empty, but failure will contain
451 In task failure, keys will be empty, but failure will contain
164 the failure object that encapsulates the remote exception.
452 the failure object that encapsulates the remote exception.
165 One can also simply call the raiseException() method of
453 One can also simply call the `raise_exception` method of
166 this class to re-raise any remote exception in the local
454 this class to re-raise any remote exception in the local
167 session.
455 session.
168
456
169 The TaskResult has a .ns member, which is a property for access
457 The `TaskResult` has a `.ns` member, which is a property for access
170 to the results. If the Task had pull=['a', 'b'], then the
458 to the results. If the Task had pull=['a', 'b'], then the
171 Task Result will have attributes tr.ns.a, tr.ns.b for those values.
459 Task Result will have attributes `tr.ns.a`, `tr.ns.b` for those values.
172 Accessing tr.ns will raise the remote failure if the task failed.
460 Accessing `tr.ns` will raise the remote failure if the task failed.
173
461
174 The engineid attribute should have the engineid of the engine
462 The `engineid` attribute should have the `engineid` of the engine
175 that ran the task. But, because engines can come and go in
463 that ran the task. But, because engines can come and go,
176 the ipython task system, the engineid may not continue to be
464 the `engineid` may not continue to be
177 valid or accurate.
465 valid or accurate.
178
466
179 The taskid attribute simply gives the taskid that the task
467 The `taskid` attribute simply gives the `taskid` that the task
180 is tracked under.
468 is tracked under.
181 """
469 """
182 taskid = None
470 taskid = None
@@ -188,7 +476,7 b' class TaskResult(object):'
188 return self._ns
476 return self._ns
189
477
190 def _setNS(self, v):
478 def _setNS(self, v):
191 raise Exception("I am protected!")
479 raise Exception("the ns attribute cannot be changed")
192
480
193 ns = property(_getNS, _setNS)
481 ns = property(_getNS, _setNS)
194
482
@@ -214,15 +502,19 b' class TaskResult(object):'
214
502
215 def __getitem__(self, key):
503 def __getitem__(self, key):
216 if self.failure is not None:
504 if self.failure is not None:
217 self.raiseException()
505 self.raise_exception()
218 return self.results[key]
506 return self.results[key]
219
507
220 def raiseException(self):
508 def raise_exception(self):
221 """Re-raise any remote exceptions in the local python session."""
509 """Re-raise any remote exceptions in the local python session."""
222 if self.failure is not None:
510 if self.failure is not None:
223 self.failure.raiseException()
511 self.failure.raiseException()
224
512
225
513
514 #-----------------------------------------------------------------------------
515 # The controller side of things
516 #-----------------------------------------------------------------------------
517
226 class IWorker(zi.Interface):
518 class IWorker(zi.Interface):
227 """The Basic Worker Interface.
519 """The Basic Worker Interface.
228
520
@@ -237,12 +529,15 b' class IWorker(zi.Interface):'
237 :Parameters:
529 :Parameters:
238 task : a `Task` object
530 task : a `Task` object
239
531
240 :Returns: `Deferred` to a `TaskResult` object.
532 :Returns: `Deferred` to a tuple of (success, result) where
533 success if a boolean that signifies success or failure
534 and result is the task result.
241 """
535 """
242
536
243
537
244 class WorkerFromQueuedEngine(object):
538 class WorkerFromQueuedEngine(object):
245 """Adapt an `IQueuedEngine` to an `IWorker` object"""
539 """Adapt an `IQueuedEngine` to an `IWorker` object"""
540
246 zi.implements(IWorker)
541 zi.implements(IWorker)
247
542
248 def __init__(self, qe):
543 def __init__(self, qe):
@@ -257,53 +552,27 b' class WorkerFromQueuedEngine(object):'
257 def run(self, task):
552 def run(self, task):
258 """Run task in worker's namespace.
553 """Run task in worker's namespace.
259
554
555 This takes a task and calls methods on the task that actually
556 cause `self.queuedEngine` to do the task. See the methods of
557 `ITask` for more information about how these methods are called.
558
260 :Parameters:
559 :Parameters:
261 task : a `Task` object
560 task : a `Task` object
262
561
263 :Returns: `Deferred` to a `TaskResult` object.
562 :Returns: `Deferred` to a tuple of (success, result) where
563 success if a boolean that signifies success or failure
564 and result is the task result.
264 """
565 """
265 if task.clear_before:
566 d = defer.succeed(None)
266 d = self.queuedEngine.reset()
567 d.addCallback(task.start_time)
267 else:
568 task.pre_task(d, self.queuedEngine)
268 d = defer.succeed(None)
569 task.submit_task(d, self.queuedEngine)
269
570 task.post_task(d, self.queuedEngine)
270 if task.push is not None:
571 d.addBoth(task.stop_time)
271 d.addCallback(lambda r: self.queuedEngine.push(task.push))
572 d.addBoth(task.process_result, self.queuedEngine.id)
272
573 # At this point, there will be (success, result) coming down the line
273 d.addCallback(lambda r: self.queuedEngine.execute(task.expression))
574 return d
274
575
275 if task.pull is not None:
276 d.addCallback(lambda r: self.queuedEngine.pull(task.pull))
277 else:
278 d.addCallback(lambda r: None)
279
280 def reseter(result):
281 self.queuedEngine.reset()
282 return result
283
284 if task.clear_after:
285 d.addBoth(reseter)
286
287 return d.addBoth(self._zipResults, task.pull, time.time(), time.localtime())
288
289 def _zipResults(self, result, names, start, start_struct):
290 """Callback for construting the TaskResult object."""
291 if isinstance(result, failure.Failure):
292 tr = TaskResult(result, self.queuedEngine.id)
293 else:
294 if names is None:
295 resultDict = {}
296 elif len(names) == 1:
297 resultDict = {names[0]:result}
298 else:
299 resultDict = dict(zip(names, result))
300 tr = TaskResult(resultDict, self.queuedEngine.id)
301 # the time info
302 tr.submitted = time.strftime(time_format, start_struct)
303 tr.completed = time.strftime(time_format)
304 tr.duration = time.time()-start
305 return tr
306
307
576
308 components.registerAdapter(WorkerFromQueuedEngine, es.IEngineQueued, IWorker)
577 components.registerAdapter(WorkerFromQueuedEngine, es.IEngineQueued, IWorker)
309
578
@@ -319,14 +588,14 b' class IScheduler(zi.Interface):'
319 """Add a task to the queue of the Scheduler.
588 """Add a task to the queue of the Scheduler.
320
589
321 :Parameters:
590 :Parameters:
322 task : a `Task` object
591 task : an `ITask` implementer
323 The task to be queued.
592 The task to be queued.
324 flags : dict
593 flags : dict
325 General keywords for more sophisticated scheduling
594 General keywords for more sophisticated scheduling
326 """
595 """
327
596
328 def pop_task(id=None):
597 def pop_task(id=None):
329 """Pops a Task object.
598 """Pops a task object from the queue.
330
599
331 This gets the next task to be run. If no `id` is requested, the highest priority
600 This gets the next task to be run. If no `id` is requested, the highest priority
332 task is returned.
601 task is returned.
@@ -336,7 +605,7 b' class IScheduler(zi.Interface):'
336 The id of the task to be popped. The default (None) is to return
605 The id of the task to be popped. The default (None) is to return
337 the highest priority task.
606 the highest priority task.
338
607
339 :Returns: a `Task` object
608 :Returns: an `ITask` implementer
340
609
341 :Exceptions:
610 :Exceptions:
342 IndexError : raised if no taskid in queue
611 IndexError : raised if no taskid in queue
@@ -346,8 +615,9 b' class IScheduler(zi.Interface):'
346 """Add a worker to the worker queue.
615 """Add a worker to the worker queue.
347
616
348 :Parameters:
617 :Parameters:
349 worker : an IWorker implementing object
618 worker : an `IWorker` implementer
350 flags : General keywords for more sophisticated scheduling
619 flags : dict
620 General keywords for more sophisticated scheduling
351 """
621 """
352
622
353 def pop_worker(id=None):
623 def pop_worker(id=None):
@@ -370,15 +640,15 b' class IScheduler(zi.Interface):'
370 """Returns True if there is something to do, False otherwise"""
640 """Returns True if there is something to do, False otherwise"""
371
641
372 def schedule():
642 def schedule():
373 """Returns a tuple of the worker and task pair for the next
643 """Returns (worker,task) pair for the next task to be run."""
374 task to be run.
375 """
376
644
377
645
378 class FIFOScheduler(object):
646 class FIFOScheduler(object):
379 """A basic First-In-First-Out (Queue) Scheduler.
647 """
380 This is the default Scheduler for the TaskController.
648 A basic First-In-First-Out (Queue) Scheduler.
381 See the docstrings for IScheduler for interface details.
649
650 This is the default Scheduler for the `TaskController`.
651 See the docstrings for `IScheduler` for interface details.
382 """
652 """
383
653
384 zi.implements(IScheduler)
654 zi.implements(IScheduler)
@@ -435,7 +705,9 b' class FIFOScheduler(object):'
435 for t in self.tasks:
705 for t in self.tasks:
436 for w in self.workers:
706 for w in self.workers:
437 try:# do not allow exceptions to break this
707 try:# do not allow exceptions to break this
438 cando = t.depend is None or t.depend(w.properties)
708 # Allow the task to check itself using its
709 # check_depend method.
710 cando = t.check_depend(w.properties)
439 except:
711 except:
440 cando = False
712 cando = False
441 if cando:
713 if cando:
@@ -445,9 +717,12 b' class FIFOScheduler(object):'
445
717
446
718
447 class LIFOScheduler(FIFOScheduler):
719 class LIFOScheduler(FIFOScheduler):
448 """A Last-In-First-Out (Stack) Scheduler. This scheduler should naively
720 """
449 reward fast engines by giving them more jobs. This risks starvation, but
721 A Last-In-First-Out (Stack) Scheduler.
450 only in cases with low load, where starvation does not really matter.
722
723 This scheduler should naively reward fast engines by giving
724 them more jobs. This risks starvation, but only in cases with
725 low load, where starvation does not really matter.
451 """
726 """
452
727
453 def add_task(self, task, **flags):
728 def add_task(self, task, **flags):
@@ -462,13 +737,15 b' class LIFOScheduler(FIFOScheduler):'
462
737
463
738
464 class ITaskController(cs.IControllerBase):
739 class ITaskController(cs.IControllerBase):
465 """The Task based interface to a `ControllerService` object
740 """
741 The Task based interface to a `ControllerService` object
466
742
467 This adapts a `ControllerService` to the ITaskController interface.
743 This adapts a `ControllerService` to the ITaskController interface.
468 """
744 """
469
745
470 def run(task):
746 def run(task):
471 """Run a task.
747 """
748 Run a task.
472
749
473 :Parameters:
750 :Parameters:
474 task : an IPython `Task` object
751 task : an IPython `Task` object
@@ -477,13 +754,14 b' class ITaskController(cs.IControllerBase):'
477 """
754 """
478
755
479 def get_task_result(taskid, block=False):
756 def get_task_result(taskid, block=False):
480 """Get the result of a task by its ID.
757 """
758 Get the result of a task by its ID.
481
759
482 :Parameters:
760 :Parameters:
483 taskid : int
761 taskid : int
484 the id of the task whose result is requested
762 the id of the task whose result is requested
485
763
486 :Returns: `Deferred` to (taskid, actualResult) if the task is done, and None
764 :Returns: `Deferred` to the task result if the task is done, and None
487 if not.
765 if not.
488
766
489 :Exceptions:
767 :Exceptions:
@@ -508,23 +786,35 b' class ITaskController(cs.IControllerBase):'
508 """
786 """
509
787
510 def barrier(taskids):
788 def barrier(taskids):
511 """Block until the list of taskids are completed.
789 """
790 Block until the list of taskids are completed.
512
791
513 Returns None on success.
792 Returns None on success.
514 """
793 """
515
794
516 def spin():
795 def spin():
517 """touch the scheduler, to resume scheduling without submitting
796 """
518 a task.
797 Touch the scheduler, to resume scheduling without submitting a task.
519 """
798 """
520
799
521 def queue_status(self, verbose=False):
800 def queue_status(verbose=False):
522 """Get a dictionary with the current state of the task queue.
801 """
802 Get a dictionary with the current state of the task queue.
523
803
524 If verbose is True, then return lists of taskids, otherwise,
804 If verbose is True, then return lists of taskids, otherwise,
525 return the number of tasks with each status.
805 return the number of tasks with each status.
526 """
806 """
527
807
808 def clear():
809 """
810 Clear all previously run tasks from the task controller.
811
812 This is needed because the task controller keep all task results
813 in memory. This can be a problem is there are many completed
814 tasks. Users should call this periodically to clean out these
815 cached task results.
816 """
817
528
818
529 class TaskController(cs.ControllerAdapterBase):
819 class TaskController(cs.ControllerAdapterBase):
530 """The Task based interface to a Controller object.
820 """The Task based interface to a Controller object.
@@ -561,7 +851,7 b' class TaskController(cs.ControllerAdapterBase):'
561 def registerWorker(self, id):
851 def registerWorker(self, id):
562 """Called by controller.register_engine."""
852 """Called by controller.register_engine."""
563 if self.workers.get(id):
853 if self.workers.get(id):
564 raise "We already have one! This should not happen."
854 raise ValueError("worker with id %s already exists. This should not happen." % id)
565 self.workers[id] = IWorker(self.controller.engines[id])
855 self.workers[id] = IWorker(self.controller.engines[id])
566 self.workers[id].workerid = id
856 self.workers[id].workerid = id
567 if not self.pendingTasks.has_key(id):# if not working
857 if not self.pendingTasks.has_key(id):# if not working
@@ -586,21 +876,25 b' class TaskController(cs.ControllerAdapterBase):'
586 #---------------------------------------------------------------------------
876 #---------------------------------------------------------------------------
587
877
588 def run(self, task):
878 def run(self, task):
589 """Run a task and return `Deferred` to its taskid."""
879 """
880 Run a task and return `Deferred` to its taskid.
881 """
590 task.taskid = self.taskid
882 task.taskid = self.taskid
591 task.start = time.localtime()
883 task.start = time.localtime()
592 self.taskid += 1
884 self.taskid += 1
593 d = defer.Deferred()
885 d = defer.Deferred()
594 self.scheduler.add_task(task)
886 self.scheduler.add_task(task)
595 # log.msg('Queuing task: %i' % task.taskid)
887 log.msg('Queuing task: %i' % task.taskid)
596
888
597 self.deferredResults[task.taskid] = []
889 self.deferredResults[task.taskid] = []
598 self.distributeTasks()
890 self.distributeTasks()
599 return defer.succeed(task.taskid)
891 return defer.succeed(task.taskid)
600
892
601 def get_task_result(self, taskid, block=False):
893 def get_task_result(self, taskid, block=False):
602 """Returns a `Deferred` to a TaskResult tuple or None."""
894 """
603 # log.msg("Getting task result: %i" % taskid)
895 Returns a `Deferred` to the task result, or None.
896 """
897 log.msg("Getting task result: %i" % taskid)
604 if self.finishedResults.has_key(taskid):
898 if self.finishedResults.has_key(taskid):
605 tr = self.finishedResults[taskid]
899 tr = self.finishedResults[taskid]
606 return defer.succeed(tr)
900 return defer.succeed(tr)
@@ -615,7 +909,9 b' class TaskController(cs.ControllerAdapterBase):'
615 return defer.fail(IndexError("task ID not registered: %r" % taskid))
909 return defer.fail(IndexError("task ID not registered: %r" % taskid))
616
910
617 def abort(self, taskid):
911 def abort(self, taskid):
618 """Remove a task from the queue if it has not been run already."""
912 """
913 Remove a task from the queue if it has not been run already.
914 """
619 if not isinstance(taskid, int):
915 if not isinstance(taskid, int):
620 return defer.fail(failure.Failure(TypeError("an integer task id expected: %r" % taskid)))
916 return defer.fail(failure.Failure(TypeError("an integer task id expected: %r" % taskid)))
621 try:
917 try:
@@ -674,8 +970,10 b' class TaskController(cs.ControllerAdapterBase):'
674 #---------------------------------------------------------------------------
970 #---------------------------------------------------------------------------
675
971
676 def _doAbort(self, taskid):
972 def _doAbort(self, taskid):
677 """Helper function for aborting a pending task."""
973 """
678 # log.msg("Task aborted: %i" % taskid)
974 Helper function for aborting a pending task.
975 """
976 log.msg("Task aborted: %i" % taskid)
679 result = failure.Failure(error.TaskAborted())
977 result = failure.Failure(error.TaskAborted())
680 self._finishTask(taskid, result)
978 self._finishTask(taskid, result)
681 if taskid in self.abortPending:
979 if taskid in self.abortPending:
@@ -683,14 +981,16 b' class TaskController(cs.ControllerAdapterBase):'
683
981
684 def _finishTask(self, taskid, result):
982 def _finishTask(self, taskid, result):
685 dlist = self.deferredResults.pop(taskid)
983 dlist = self.deferredResults.pop(taskid)
686 result.taskid = taskid # The TaskResult should save the taskid
984 # result.taskid = taskid # The TaskResult should save the taskid
687 self.finishedResults[taskid] = result
985 self.finishedResults[taskid] = result
688 for d in dlist:
986 for d in dlist:
689 d.callback(result)
987 d.callback(result)
690
988
691 def distributeTasks(self):
989 def distributeTasks(self):
692 """Distribute tasks while self.scheduler has things to do."""
990 """
693 # log.msg("distributing Tasks")
991 Distribute tasks while self.scheduler has things to do.
992 """
993 log.msg("distributing Tasks")
694 worker, task = self.scheduler.schedule()
994 worker, task = self.scheduler.schedule()
695 if not worker and not task:
995 if not worker and not task:
696 if self.idleLater and self.idleLater.called:# we are inside failIdle
996 if self.idleLater and self.idleLater.called:# we are inside failIdle
@@ -705,7 +1005,7 b' class TaskController(cs.ControllerAdapterBase):'
705 self.pendingTasks[worker.workerid] = task
1005 self.pendingTasks[worker.workerid] = task
706 # run/link callbacks
1006 # run/link callbacks
707 d = worker.run(task)
1007 d = worker.run(task)
708 # log.msg("Running task %i on worker %i" %(task.taskid, worker.workerid))
1008 log.msg("Running task %i on worker %i" %(task.taskid, worker.workerid))
709 d.addBoth(self.taskCompleted, task.taskid, worker.workerid)
1009 d.addBoth(self.taskCompleted, task.taskid, worker.workerid)
710 worker, task = self.scheduler.schedule()
1010 worker, task = self.scheduler.schedule()
711 # check for idle timeout:
1011 # check for idle timeout:
@@ -727,14 +1027,15 b' class TaskController(cs.ControllerAdapterBase):'
727 t = self.scheduler.pop_task()
1027 t = self.scheduler.pop_task()
728 msg = "task %i failed to execute due to unmet dependencies"%t.taskid
1028 msg = "task %i failed to execute due to unmet dependencies"%t.taskid
729 msg += " for %i seconds"%self.timeout
1029 msg += " for %i seconds"%self.timeout
730 # log.msg("Task aborted by timeout: %i" % t.taskid)
1030 log.msg("Task aborted by timeout: %i" % t.taskid)
731 f = failure.Failure(error.TaskTimeout(msg))
1031 f = failure.Failure(error.TaskTimeout(msg))
732 self._finishTask(t.taskid, f)
1032 self._finishTask(t.taskid, f)
733 self.idleLater = None
1033 self.idleLater = None
734
1034
735
1035
736 def taskCompleted(self, result, taskid, workerid):
1036 def taskCompleted(self, success_and_result, taskid, workerid):
737 """This is the err/callback for a completed task."""
1037 """This is the err/callback for a completed task."""
1038 success, result = success_and_result
738 try:
1039 try:
739 task = self.pendingTasks.pop(workerid)
1040 task = self.pendingTasks.pop(workerid)
740 except:
1041 except:
@@ -751,7 +1052,7 b' class TaskController(cs.ControllerAdapterBase):'
751 aborted = True
1052 aborted = True
752
1053
753 if not aborted:
1054 if not aborted:
754 if result.failure is not None and isinstance(result.failure, failure.Failure): # we failed
1055 if not success:
755 log.msg("Task %i failed on worker %i"% (taskid, workerid))
1056 log.msg("Task %i failed on worker %i"% (taskid, workerid))
756 if task.retries > 0: # resubmit
1057 if task.retries > 0: # resubmit
757 task.retries -= 1
1058 task.retries -= 1
@@ -759,7 +1060,7 b' class TaskController(cs.ControllerAdapterBase):'
759 s = "Resubmitting task %i, %i retries remaining" %(taskid, task.retries)
1060 s = "Resubmitting task %i, %i retries remaining" %(taskid, task.retries)
760 log.msg(s)
1061 log.msg(s)
761 self.distributeTasks()
1062 self.distributeTasks()
762 elif isinstance(task.recovery_task, Task) and \
1063 elif isinstance(task.recovery_task, BaseTask) and \
763 task.recovery_task.retries > -1:
1064 task.recovery_task.retries > -1:
764 # retries = -1 is to prevent infinite recovery_task loop
1065 # retries = -1 is to prevent infinite recovery_task loop
765 task.retries = -1
1066 task.retries = -1
@@ -775,17 +1076,18 b' class TaskController(cs.ControllerAdapterBase):'
775 # it may have died, and not yet been unregistered
1076 # it may have died, and not yet been unregistered
776 reactor.callLater(self.failurePenalty, self.readmitWorker, workerid)
1077 reactor.callLater(self.failurePenalty, self.readmitWorker, workerid)
777 else: # we succeeded
1078 else: # we succeeded
778 # log.msg("Task completed: %i"% taskid)
1079 log.msg("Task completed: %i"% taskid)
779 self._finishTask(taskid, result)
1080 self._finishTask(taskid, result)
780 self.readmitWorker(workerid)
1081 self.readmitWorker(workerid)
781 else:# we aborted the task
1082 else: # we aborted the task
782 if result.failure is not None and isinstance(result.failure, failure.Failure): # it failed, penalize worker
1083 if not success:
783 reactor.callLater(self.failurePenalty, self.readmitWorker, workerid)
1084 reactor.callLater(self.failurePenalty, self.readmitWorker, workerid)
784 else:
1085 else:
785 self.readmitWorker(workerid)
1086 self.readmitWorker(workerid)
786
1087
787 def readmitWorker(self, workerid):
1088 def readmitWorker(self, workerid):
788 """Readmit a worker to the scheduler.
1089 """
1090 Readmit a worker to the scheduler.
789
1091
790 This is outside `taskCompleted` because of the `failurePenalty` being
1092 This is outside `taskCompleted` because of the `failurePenalty` being
791 implemented through `reactor.callLater`.
1093 implemented through `reactor.callLater`.
@@ -794,6 +1096,18 b' class TaskController(cs.ControllerAdapterBase):'
794 if workerid in self.workers.keys() and workerid not in self.pendingTasks.keys():
1096 if workerid in self.workers.keys() and workerid not in self.pendingTasks.keys():
795 self.scheduler.add_worker(self.workers[workerid])
1097 self.scheduler.add_worker(self.workers[workerid])
796 self.distributeTasks()
1098 self.distributeTasks()
1099
1100 def clear(self):
1101 """
1102 Clear all previously run tasks from the task controller.
1103
1104 This is needed because the task controller keep all task results
1105 in memory. This can be a problem is there are many completed
1106 tasks. Users should call this periodically to clean out these
1107 cached task results.
1108 """
1109 self.finishedResults = {}
1110 return defer.succeed(None)
797
1111
798
1112
799 components.registerAdapter(TaskController, cs.IControllerBase, ITaskController)
1113 components.registerAdapter(TaskController, cs.IControllerBase, ITaskController)
@@ -1,9 +1,8 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 # -*- test-case-name: IPython.kernel.tests.test_taskcontrollerxmlrpc -*-
2 # -*- test-case-name: IPython.kernel.tests.test_taskcontrollerxmlrpc -*-
3
3
4 """The Generic Task Client object.
4 """
5
5 A blocking version of the task client.
6 This must be subclassed based on your connection method.
7 """
6 """
8
7
9 __docformat__ = "restructuredtext en"
8 __docformat__ = "restructuredtext en"
@@ -24,119 +23,100 b' from twisted.python import components, log'
24
23
25 from IPython.kernel.twistedutil import blockingCallFromThread
24 from IPython.kernel.twistedutil import blockingCallFromThread
26 from IPython.kernel import task, error
25 from IPython.kernel import task, error
26 from IPython.kernel.mapper import (
27 SynchronousTaskMapper,
28 ITaskMapperFactory,
29 IMapper
30 )
31 from IPython.kernel.parallelfunction import (
32 ParallelFunction,
33 ITaskParallelDecorator
34 )
27
35
28 #-------------------------------------------------------------------------------
36 #-------------------------------------------------------------------------------
29 # Connecting Task Client
37 # The task client
30 #-------------------------------------------------------------------------------
38 #-------------------------------------------------------------------------------
31
39
32 class InteractiveTaskClient(object):
33
34 def irun(self, *args, **kwargs):
35 """Run a task on the `TaskController`.
36
37 This method is a shorthand for run(task) and its arguments are simply
38 passed onto a `Task` object:
39
40 irun(*args, **kwargs) -> run(Task(*args, **kwargs))
41
42 :Parameters:
43 expression : str
44 A str that is valid python code that is the task.
45 pull : str or list of str
46 The names of objects to be pulled as results.
47 push : dict
48 A dict of objects to be pushed into the engines namespace before
49 execution of the expression.
50 clear_before : boolean
51 Should the engine's namespace be cleared before the task is run.
52 Default=False.
53 clear_after : boolean
54 Should the engine's namespace be cleared after the task is run.
55 Default=False.
56 retries : int
57 The number of times to resumbit the task if it fails. Default=0.
58 options : dict
59 Any other keyword options for more elaborate uses of tasks
60
61 :Returns: A `TaskResult` object.
62 """
63 block = kwargs.pop('block', False)
64 if len(args) == 1 and isinstance(args[0], task.Task):
65 t = args[0]
66 else:
67 t = task.Task(*args, **kwargs)
68 taskid = self.run(t)
69 print "TaskID = %i"%taskid
70 if block:
71 return self.get_task_result(taskid, block)
72 else:
73 return taskid
74
75 class IBlockingTaskClient(Interface):
40 class IBlockingTaskClient(Interface):
76 """
41 """
77 An interface for blocking task clients.
42 A vague interface of the blocking task client
78 """
43 """
79 pass
44 pass
80
45
81
46 class BlockingTaskClient(object):
82 class BlockingTaskClient(InteractiveTaskClient):
83 """
47 """
84 This class provides a blocking task client.
48 A blocking task client that adapts a non-blocking one.
85 """
49 """
86
50
87 implements(IBlockingTaskClient)
51 implements(
52 IBlockingTaskClient,
53 ITaskMapperFactory,
54 IMapper,
55 ITaskParallelDecorator
56 )
88
57
89 def __init__(self, task_controller):
58 def __init__(self, task_controller):
90 self.task_controller = task_controller
59 self.task_controller = task_controller
91 self.block = True
60 self.block = True
92
61
93 def run(self, task):
62 def run(self, task, block=False):
94 """
63 """Run a task on the `TaskController`.
95 Run a task and return a task id that can be used to get the task result.
64
65 See the documentation of the `MapTask` and `StringTask` classes for
66 details on how to build a task of different types.
96
67
97 :Parameters:
68 :Parameters:
98 task : `Task`
69 task : an `ITask` implementer
99 The `Task` object to run
70
71 :Returns: The int taskid of the submitted task. Pass this to
72 `get_task_result` to get the `TaskResult` object.
100 """
73 """
101 return blockingCallFromThread(self.task_controller.run, task)
74 tid = blockingCallFromThread(self.task_controller.run, task)
75 if block:
76 return self.get_task_result(tid, block=True)
77 else:
78 return tid
102
79
103 def get_task_result(self, taskid, block=False):
80 def get_task_result(self, taskid, block=False):
104 """
81 """
105 Get or poll for a task result.
82 Get a task result by taskid.
106
83
107 :Parameters:
84 :Parameters:
108 taskid : int
85 taskid : int
109 The id of the task whose result to get
86 The taskid of the task to be retrieved.
110 block : boolean
87 block : boolean
111 If True, wait until the task is done and then result the
88 Should I block until the task is done?
112 `TaskResult` object. If False, just poll for the result and
89
113 return None if the task is not done.
90 :Returns: A `TaskResult` object that encapsulates the task result.
114 """
91 """
115 return blockingCallFromThread(self.task_controller.get_task_result,
92 return blockingCallFromThread(self.task_controller.get_task_result,
116 taskid, block)
93 taskid, block)
117
94
118 def abort(self, taskid):
95 def abort(self, taskid):
119 """
96 """
120 Abort a task by task id if it has not been started.
97 Abort a task by taskid.
98
99 :Parameters:
100 taskid : int
101 The taskid of the task to be aborted.
121 """
102 """
122 return blockingCallFromThread(self.task_controller.abort, taskid)
103 return blockingCallFromThread(self.task_controller.abort, taskid)
123
104
124 def barrier(self, taskids):
105 def barrier(self, taskids):
125 """
106 """Block until a set of tasks are completed.
126 Wait for a set of tasks to finish.
127
107
128 :Parameters:
108 :Parameters:
129 taskids : list of ints
109 taskids : list, tuple
130 A list of task ids to wait for.
110 A sequence of taskids to block on.
131 """
111 """
132 return blockingCallFromThread(self.task_controller.barrier, taskids)
112 return blockingCallFromThread(self.task_controller.barrier, taskids)
133
113
134 def spin(self):
114 def spin(self):
135 """
115 """
136 Cause the scheduler to schedule tasks.
116 Touch the scheduler, to resume scheduling without submitting a task.
137
117
138 This method only needs to be called in unusual situations where the
118 This method only needs to be called in unusual situations where the
139 scheduler is idle for some reason.
119 scheduler is idle for some reason.
140 """
120 """
141 return blockingCallFromThread(self.task_controller.spin)
121 return blockingCallFromThread(self.task_controller.spin)
142
122
@@ -153,7 +133,46 b' class BlockingTaskClient(InteractiveTaskClient):'
153 A dict with the queue status.
133 A dict with the queue status.
154 """
134 """
155 return blockingCallFromThread(self.task_controller.queue_status, verbose)
135 return blockingCallFromThread(self.task_controller.queue_status, verbose)
136
137 def clear(self):
138 """
139 Clear all previously run tasks from the task controller.
140
141 This is needed because the task controller keep all task results
142 in memory. This can be a problem is there are many completed
143 tasks. Users should call this periodically to clean out these
144 cached task results.
145 """
146 return blockingCallFromThread(self.task_controller.clear)
147
148 def map(self, func, *sequences):
149 """
150 Apply func to *sequences elementwise. Like Python's builtin map.
151
152 This version is load balanced.
153 """
154 return self.mapper().map(func, *sequences)
156
155
156 def mapper(self, clear_before=False, clear_after=False, retries=0,
157 recovery_task=None, depend=None, block=True):
158 """
159 Create an `IMapper` implementer with a given set of arguments.
160
161 The `IMapper` created using a task controller is load balanced.
162
163 See the documentation for `IPython.kernel.task.BaseTask` for
164 documentation on the arguments to this method.
165 """
166 return SynchronousTaskMapper(self, clear_before=clear_before,
167 clear_after=clear_after, retries=retries,
168 recovery_task=recovery_task, depend=depend, block=block)
169
170 def parallel(self, clear_before=False, clear_after=False, retries=0,
171 recovery_task=None, depend=None, block=True):
172 mapper = self.mapper(clear_before, clear_after, retries,
173 recovery_task, depend, block)
174 pf = ParallelFunction(mapper)
175 return pf
157
176
158 components.registerAdapter(BlockingTaskClient,
177 components.registerAdapter(BlockingTaskClient,
159 task.ITaskController, IBlockingTaskClient)
178 task.ITaskController, IBlockingTaskClient)
@@ -34,6 +34,15 b' from IPython.kernel.clientinterfaces import ('
34 IFCClientInterfaceProvider,
34 IFCClientInterfaceProvider,
35 IBlockingClientAdaptor
35 IBlockingClientAdaptor
36 )
36 )
37 from IPython.kernel.mapper import (
38 TaskMapper,
39 ITaskMapperFactory,
40 IMapper
41 )
42 from IPython.kernel.parallelfunction import (
43 ParallelFunction,
44 ITaskParallelDecorator
45 )
37
46
38 #-------------------------------------------------------------------------------
47 #-------------------------------------------------------------------------------
39 # The Controller side of things
48 # The Controller side of things
@@ -43,32 +52,38 b' from IPython.kernel.clientinterfaces import ('
43 class IFCTaskController(Interface):
52 class IFCTaskController(Interface):
44 """Foolscap interface to task controller.
53 """Foolscap interface to task controller.
45
54
46 See the documentation of ITaskController for documentation about the methods.
55 See the documentation of `ITaskController` for more information.
47 """
56 """
48 def remote_run(request, binTask):
57 def remote_run(binTask):
49 """"""
58 """"""
50
59
51 def remote_abort(request, taskid):
60 def remote_abort(taskid):
52 """"""
61 """"""
53
62
54 def remote_get_task_result(request, taskid, block=False):
63 def remote_get_task_result(taskid, block=False):
55 """"""
64 """"""
56
65
57 def remote_barrier(request, taskids):
66 def remote_barrier(taskids):
67 """"""
68
69 def remote_spin():
58 """"""
70 """"""
59
71
60 def remote_spin(request):
72 def remote_queue_status(verbose):
61 """"""
73 """"""
62
74
63 def remote_queue_status(request, verbose):
75 def remote_clear():
64 """"""
76 """"""
65
77
66
78
67 class FCTaskControllerFromTaskController(Referenceable):
79 class FCTaskControllerFromTaskController(Referenceable):
68 """XML-RPC attachmeot for controller.
69
70 See IXMLRPCTaskController and ITaskController (and its children) for documentation.
71 """
80 """
81 Adapt a `TaskController` to an `IFCTaskController`
82
83 This class is used to expose a `TaskController` over the wire using
84 the Foolscap network protocol.
85 """
86
72 implements(IFCTaskController, IFCClientInterfaceProvider)
87 implements(IFCTaskController, IFCClientInterfaceProvider)
73
88
74 def __init__(self, taskController):
89 def __init__(self, taskController):
@@ -92,8 +107,8 b' class FCTaskControllerFromTaskController(Referenceable):'
92
107
93 def remote_run(self, ptask):
108 def remote_run(self, ptask):
94 try:
109 try:
95 ctask = pickle.loads(ptask)
110 task = pickle.loads(ptask)
96 task = taskmodule.uncanTask(ctask)
111 task.uncan_task()
97 except:
112 except:
98 d = defer.fail(pickle.UnpickleableError("Could not unmarshal task"))
113 d = defer.fail(pickle.UnpickleableError("Could not unmarshal task"))
99 else:
114 else:
@@ -132,6 +147,9 b' class FCTaskControllerFromTaskController(Referenceable):'
132 d.addErrback(self.packageFailure)
147 d.addErrback(self.packageFailure)
133 return d
148 return d
134
149
150 def remote_clear(self):
151 return self.taskController.clear()
152
135 def remote_get_client_name(self):
153 def remote_get_client_name(self):
136 return 'IPython.kernel.taskfc.FCTaskClient'
154 return 'IPython.kernel.taskfc.FCTaskClient'
137
155
@@ -144,13 +162,23 b' components.registerAdapter(FCTaskControllerFromTaskController,'
144 #-------------------------------------------------------------------------------
162 #-------------------------------------------------------------------------------
145
163
146 class FCTaskClient(object):
164 class FCTaskClient(object):
147 """XML-RPC based TaskController client that implements ITaskController.
148
149 :Parameters:
150 addr : (ip, port)
151 The ip (str) and port (int) tuple of the `TaskController`.
152 """
165 """
153 implements(taskmodule.ITaskController, IBlockingClientAdaptor)
166 Client class for Foolscap exposed `TaskController`.
167
168 This class is an adapter that makes a `RemoteReference` to a
169 `TaskController` look like an actual `ITaskController` on the client side.
170
171 This class also implements `IBlockingClientAdaptor` so that clients can
172 automatically get a blocking version of this class.
173 """
174
175 implements(
176 taskmodule.ITaskController,
177 IBlockingClientAdaptor,
178 ITaskMapperFactory,
179 IMapper,
180 ITaskParallelDecorator
181 )
154
182
155 def __init__(self, remote_reference):
183 def __init__(self, remote_reference):
156 self.remote_reference = remote_reference
184 self.remote_reference = remote_reference
@@ -168,48 +196,26 b' class FCTaskClient(object):'
168 def run(self, task):
196 def run(self, task):
169 """Run a task on the `TaskController`.
197 """Run a task on the `TaskController`.
170
198
171 :Parameters:
199 See the documentation of the `MapTask` and `StringTask` classes for
172 task : a `Task` object
200 details on how to build a task of different types.
173
174 The Task object is created using the following signature:
175
176 Task(expression, pull=None, push={}, clear_before=False,
177 clear_after=False, retries=0, **options):)
178
201
179 The meaning of the arguments is as follows:
202 :Parameters:
203 task : an `ITask` implementer
180
204
181 :Task Parameters:
182 expression : str
183 A str that is valid python code that is the task.
184 pull : str or list of str
185 The names of objects to be pulled as results.
186 push : dict
187 A dict of objects to be pushed into the engines namespace before
188 execution of the expression.
189 clear_before : boolean
190 Should the engine's namespace be cleared before the task is run.
191 Default=False.
192 clear_after : boolean
193 Should the engine's namespace be cleared after the task is run.
194 Default=False.
195 retries : int
196 The number of times to resumbit the task if it fails. Default=0.
197 options : dict
198 Any other keyword options for more elaborate uses of tasks
199
200 :Returns: The int taskid of the submitted task. Pass this to
205 :Returns: The int taskid of the submitted task. Pass this to
201 `get_task_result` to get the `TaskResult` object.
206 `get_task_result` to get the `TaskResult` object.
202 """
207 """
203 assert isinstance(task, taskmodule.Task), "task must be a Task object!"
208 assert isinstance(task, taskmodule.BaseTask), "task must be a Task object!"
204 ctask = taskmodule.canTask(task) # handles arbitrary function in .depend
209 task.can_task()
205 # as well as arbitrary recovery_task chains
210 ptask = pickle.dumps(task, 2)
206 ptask = pickle.dumps(ctask, 2)
211 task.uncan_task()
207 d = self.remote_reference.callRemote('run', ptask)
212 d = self.remote_reference.callRemote('run', ptask)
208 d.addCallback(self.unpackage)
213 d.addCallback(self.unpackage)
209 return d
214 return d
210
215
211 def get_task_result(self, taskid, block=False):
216 def get_task_result(self, taskid, block=False):
212 """The task result by taskid.
217 """
218 Get a task result by taskid.
213
219
214 :Parameters:
220 :Parameters:
215 taskid : int
221 taskid : int
@@ -224,20 +230,19 b' class FCTaskClient(object):'
224 return d
230 return d
225
231
226 def abort(self, taskid):
232 def abort(self, taskid):
227 """Abort a task by taskid.
233 """
234 Abort a task by taskid.
228
235
229 :Parameters:
236 :Parameters:
230 taskid : int
237 taskid : int
231 The taskid of the task to be aborted.
238 The taskid of the task to be aborted.
232 block : boolean
233 Should I block until the task is aborted.
234 """
239 """
235 d = self.remote_reference.callRemote('abort', taskid)
240 d = self.remote_reference.callRemote('abort', taskid)
236 d.addCallback(self.unpackage)
241 d.addCallback(self.unpackage)
237 return d
242 return d
238
243
239 def barrier(self, taskids):
244 def barrier(self, taskids):
240 """Block until all tasks are completed.
245 """Block until a set of tasks are completed.
241
246
242 :Parameters:
247 :Parameters:
243 taskids : list, tuple
248 taskids : list, tuple
@@ -248,20 +253,77 b' class FCTaskClient(object):'
248 return d
253 return d
249
254
250 def spin(self):
255 def spin(self):
251 """touch the scheduler, to resume scheduling without submitting
256 """
252 a task.
257 Touch the scheduler, to resume scheduling without submitting a task.
258
259 This method only needs to be called in unusual situations where the
260 scheduler is idle for some reason.
253 """
261 """
254 d = self.remote_reference.callRemote('spin')
262 d = self.remote_reference.callRemote('spin')
255 d.addCallback(self.unpackage)
263 d.addCallback(self.unpackage)
256 return d
264 return d
257
265
258 def queue_status(self, verbose=False):
266 def queue_status(self, verbose=False):
259 """Return a dict with the status of the task queue."""
267 """
268 Get a dictionary with the current state of the task queue.
269
270 :Parameters:
271 verbose : boolean
272 If True, return a list of taskids. If False, simply give
273 the number of tasks with each status.
274
275 :Returns:
276 A dict with the queue status.
277 """
260 d = self.remote_reference.callRemote('queue_status', verbose)
278 d = self.remote_reference.callRemote('queue_status', verbose)
261 d.addCallback(self.unpackage)
279 d.addCallback(self.unpackage)
262 return d
280 return d
263
281
282 def clear(self):
283 """
284 Clear all previously run tasks from the task controller.
285
286 This is needed because the task controller keep all task results
287 in memory. This can be a problem is there are many completed
288 tasks. Users should call this periodically to clean out these
289 cached task results.
290 """
291 d = self.remote_reference.callRemote('clear')
292 return d
293
264 def adapt_to_blocking_client(self):
294 def adapt_to_blocking_client(self):
295 """
296 Wrap self in a blocking version that implements `IBlockingTaskClient.
297 """
265 from IPython.kernel.taskclient import IBlockingTaskClient
298 from IPython.kernel.taskclient import IBlockingTaskClient
266 return IBlockingTaskClient(self)
299 return IBlockingTaskClient(self)
300
301 def map(self, func, *sequences):
302 """
303 Apply func to *sequences elementwise. Like Python's builtin map.
304
305 This version is load balanced.
306 """
307 return self.mapper().map(func, *sequences)
308
309 def mapper(self, clear_before=False, clear_after=False, retries=0,
310 recovery_task=None, depend=None, block=True):
311 """
312 Create an `IMapper` implementer with a given set of arguments.
313
314 The `IMapper` created using a task controller is load balanced.
315
316 See the documentation for `IPython.kernel.task.BaseTask` for
317 documentation on the arguments to this method.
318 """
319 return TaskMapper(self, clear_before=clear_before,
320 clear_after=clear_after, retries=retries,
321 recovery_task=recovery_task, depend=depend, block=block)
322
323 def parallel(self, clear_before=False, clear_after=False, retries=0,
324 recovery_task=None, depend=None, block=True):
325 mapper = self.mapper(clear_before, clear_after, retries,
326 recovery_task, depend, block)
327 pf = ParallelFunction(mapper)
328 return pf
267
329
@@ -163,7 +163,6 b' class IEngineCoreTestCase(object):'
163 try:
163 try:
164 import numpy
164 import numpy
165 except:
165 except:
166 print 'no numpy, ',
167 return
166 return
168 a = numpy.random.random(1000)
167 a = numpy.random.random(1000)
169 d = self.engine.push(dict(a=a))
168 d = self.engine.push(dict(a=a))
@@ -733,7 +733,7 b' class ISynchronousMultiEngineCoordinatorTestCase(IMultiEngineCoordinatorTestCase'
733 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
733 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
734 d.addCallback(lambda r: self.assertEquals(r, range(16)))
734 d.addCallback(lambda r: self.assertEquals(r, range(16)))
735 return d
735 return d
736
736
737 def testScatterGatherNumpyNonblocking(self):
737 def testScatterGatherNumpyNonblocking(self):
738 try:
738 try:
739 import numpy
739 import numpy
@@ -749,17 +749,7 b' class ISynchronousMultiEngineCoordinatorTestCase(IMultiEngineCoordinatorTestCase'
749 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
749 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
750 d.addCallback(lambda r: assert_array_equal(r, a))
750 d.addCallback(lambda r: assert_array_equal(r, a))
751 return d
751 return d
752
752
753 def testMapNonblocking(self):
754 self.addEngine(4)
755 def f(x):
756 return x**2
757 data = range(16)
758 d= self.multiengine.map(f, data, block=False)
759 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
760 d.addCallback(lambda r: self.assertEquals(r,[f(x) for x in data]))
761 return d
762
763 def test_clear_pending_deferreds(self):
753 def test_clear_pending_deferreds(self):
764 self.addEngine(4)
754 self.addEngine(4)
765 did_list = []
755 did_list = []
@@ -43,23 +43,23 b' class TaskTestBase(object):'
43
43
44 class ITaskControllerTestCase(TaskTestBase):
44 class ITaskControllerTestCase(TaskTestBase):
45
45
46 def testTaskIDs(self):
46 def test_task_ids(self):
47 self.addEngine(1)
47 self.addEngine(1)
48 d = self.tc.run(task.Task('a=5'))
48 d = self.tc.run(task.StringTask('a=5'))
49 d.addCallback(lambda r: self.assertEquals(r, 0))
49 d.addCallback(lambda r: self.assertEquals(r, 0))
50 d.addCallback(lambda r: self.tc.run(task.Task('a=5')))
50 d.addCallback(lambda r: self.tc.run(task.StringTask('a=5')))
51 d.addCallback(lambda r: self.assertEquals(r, 1))
51 d.addCallback(lambda r: self.assertEquals(r, 1))
52 d.addCallback(lambda r: self.tc.run(task.Task('a=5')))
52 d.addCallback(lambda r: self.tc.run(task.StringTask('a=5')))
53 d.addCallback(lambda r: self.assertEquals(r, 2))
53 d.addCallback(lambda r: self.assertEquals(r, 2))
54 d.addCallback(lambda r: self.tc.run(task.Task('a=5')))
54 d.addCallback(lambda r: self.tc.run(task.StringTask('a=5')))
55 d.addCallback(lambda r: self.assertEquals(r, 3))
55 d.addCallback(lambda r: self.assertEquals(r, 3))
56 return d
56 return d
57
57
58 def testAbort(self):
58 def test_abort(self):
59 """Cannot do a proper abort test, because blocking execution prevents
59 """Cannot do a proper abort test, because blocking execution prevents
60 abort from being called before task completes"""
60 abort from being called before task completes"""
61 self.addEngine(1)
61 self.addEngine(1)
62 t = task.Task('a=5')
62 t = task.StringTask('a=5')
63 d = self.tc.abort(0)
63 d = self.tc.abort(0)
64 d.addErrback(lambda f: self.assertRaises(IndexError, f.raiseException))
64 d.addErrback(lambda f: self.assertRaises(IndexError, f.raiseException))
65 d.addCallback(lambda _:self.tc.run(t))
65 d.addCallback(lambda _:self.tc.run(t))
@@ -67,15 +67,15 b' class ITaskControllerTestCase(TaskTestBase):'
67 d.addErrback(lambda f: self.assertRaises(IndexError, f.raiseException))
67 d.addErrback(lambda f: self.assertRaises(IndexError, f.raiseException))
68 return d
68 return d
69
69
70 def testAbortType(self):
70 def test_abort_type(self):
71 self.addEngine(1)
71 self.addEngine(1)
72 d = self.tc.abort('asdfadsf')
72 d = self.tc.abort('asdfadsf')
73 d.addErrback(lambda f: self.assertRaises(TypeError, f.raiseException))
73 d.addErrback(lambda f: self.assertRaises(TypeError, f.raiseException))
74 return d
74 return d
75
75
76 def testClears(self):
76 def test_clear_before_and_after(self):
77 self.addEngine(1)
77 self.addEngine(1)
78 t = task.Task('a=1', clear_before=True, pull='b', clear_after=True)
78 t = task.StringTask('a=1', clear_before=True, pull='b', clear_after=True)
79 d = self.multiengine.execute('b=1', targets=0)
79 d = self.multiengine.execute('b=1', targets=0)
80 d.addCallback(lambda _: self.tc.run(t))
80 d.addCallback(lambda _: self.tc.run(t))
81 d.addCallback(lambda tid: self.tc.get_task_result(tid,block=True))
81 d.addCallback(lambda tid: self.tc.get_task_result(tid,block=True))
@@ -85,10 +85,10 b' class ITaskControllerTestCase(TaskTestBase):'
85 d.addErrback(lambda f: self.assertRaises(NameError, _raise_it, f))
85 d.addErrback(lambda f: self.assertRaises(NameError, _raise_it, f))
86 return d
86 return d
87
87
88 def testSimpleRetries(self):
88 def test_simple_retries(self):
89 self.addEngine(1)
89 self.addEngine(1)
90 t = task.Task("i += 1\nassert i == 16", pull='i',retries=10)
90 t = task.StringTask("i += 1\nassert i == 16", pull='i',retries=10)
91 t2 = task.Task("i += 1\nassert i == 16", pull='i',retries=10)
91 t2 = task.StringTask("i += 1\nassert i == 16", pull='i',retries=10)
92 d = self.multiengine.execute('i=0', targets=0)
92 d = self.multiengine.execute('i=0', targets=0)
93 d.addCallback(lambda r: self.tc.run(t))
93 d.addCallback(lambda r: self.tc.run(t))
94 d.addCallback(self.tc.get_task_result, block=True)
94 d.addCallback(self.tc.get_task_result, block=True)
@@ -101,10 +101,10 b' class ITaskControllerTestCase(TaskTestBase):'
101 d.addCallback(lambda r: self.assertEquals(r, 16))
101 d.addCallback(lambda r: self.assertEquals(r, 16))
102 return d
102 return d
103
103
104 def testRecoveryTasks(self):
104 def test_recovery_tasks(self):
105 self.addEngine(1)
105 self.addEngine(1)
106 t = task.Task("i=16", pull='i')
106 t = task.StringTask("i=16", pull='i')
107 t2 = task.Task("raise Exception", recovery_task=t, retries = 2)
107 t2 = task.StringTask("raise Exception", recovery_task=t, retries = 2)
108
108
109 d = self.tc.run(t2)
109 d = self.tc.run(t2)
110 d.addCallback(self.tc.get_task_result, block=True)
110 d.addCallback(self.tc.get_task_result, block=True)
@@ -112,47 +112,76 b' class ITaskControllerTestCase(TaskTestBase):'
112 d.addCallback(lambda r: self.assertEquals(r, 16))
112 d.addCallback(lambda r: self.assertEquals(r, 16))
113 return d
113 return d
114
114
115 # def testInfiniteRecoveryLoop(self):
115 def test_setup_ns(self):
116 # self.addEngine(1)
117 # t = task.Task("raise Exception", retries = 5)
118 # t2 = task.Task("assert True", retries = 2, recovery_task = t)
119 # t.recovery_task = t2
120 #
121 # d = self.tc.run(t)
122 # d.addCallback(self.tc.get_task_result, block=True)
123 # d.addCallback(lambda tr: tr.ns.i)
124 # d.addBoth(printer)
125 # d.addErrback(lambda f: self.assertRaises(AssertionError, f.raiseException))
126 # return d
127 #
128 def testSetupNS(self):
129 self.addEngine(1)
116 self.addEngine(1)
130 d = self.multiengine.execute('a=0', targets=0)
117 d = self.multiengine.execute('a=0', targets=0)
131 ns = dict(a=1, b=0)
118 ns = dict(a=1, b=0)
132 t = task.Task("", push=ns, pull=['a','b'])
119 t = task.StringTask("", push=ns, pull=['a','b'])
133 d.addCallback(lambda r: self.tc.run(t))
120 d.addCallback(lambda r: self.tc.run(t))
134 d.addCallback(self.tc.get_task_result, block=True)
121 d.addCallback(self.tc.get_task_result, block=True)
135 d.addCallback(lambda tr: {'a':tr.ns.a, 'b':tr['b']})
122 d.addCallback(lambda tr: {'a':tr.ns.a, 'b':tr['b']})
136 d.addCallback(lambda r: self.assertEquals(r, ns))
123 d.addCallback(lambda r: self.assertEquals(r, ns))
137 return d
124 return d
138
125
139 def testTaskResults(self):
126 def test_string_task_results(self):
140 self.addEngine(1)
127 self.addEngine(1)
141 t1 = task.Task('a=5', pull='a')
128 t1 = task.StringTask('a=5', pull='a')
142 d = self.tc.run(t1)
129 d = self.tc.run(t1)
143 d.addCallback(self.tc.get_task_result, block=True)
130 d.addCallback(self.tc.get_task_result, block=True)
144 d.addCallback(lambda tr: (tr.ns.a,tr['a'],tr.failure, tr.raiseException()))
131 d.addCallback(lambda tr: (tr.ns.a,tr['a'],tr.failure, tr.raise_exception()))
145 d.addCallback(lambda r: self.assertEquals(r, (5,5,None,None)))
132 d.addCallback(lambda r: self.assertEquals(r, (5,5,None,None)))
146
133
147 t2 = task.Task('7=5')
134 t2 = task.StringTask('7=5')
148 d.addCallback(lambda r: self.tc.run(t2))
135 d.addCallback(lambda r: self.tc.run(t2))
149 d.addCallback(self.tc.get_task_result, block=True)
136 d.addCallback(self.tc.get_task_result, block=True)
150 d.addCallback(lambda tr: tr.ns)
137 d.addCallback(lambda tr: tr.ns)
151 d.addErrback(lambda f: self.assertRaises(SyntaxError, f.raiseException))
138 d.addErrback(lambda f: self.assertRaises(SyntaxError, f.raiseException))
152
139
153 t3 = task.Task('', pull='b')
140 t3 = task.StringTask('', pull='b')
154 d.addCallback(lambda r: self.tc.run(t3))
141 d.addCallback(lambda r: self.tc.run(t3))
155 d.addCallback(self.tc.get_task_result, block=True)
142 d.addCallback(self.tc.get_task_result, block=True)
156 d.addCallback(lambda tr: tr.ns)
143 d.addCallback(lambda tr: tr.ns)
157 d.addErrback(lambda f: self.assertRaises(NameError, f.raiseException))
144 d.addErrback(lambda f: self.assertRaises(NameError, f.raiseException))
158 return d
145 return d
146
147 def test_map_task(self):
148 self.addEngine(1)
149 t1 = task.MapTask(lambda x: 2*x,(10,))
150 d = self.tc.run(t1)
151 d.addCallback(self.tc.get_task_result, block=True)
152 d.addCallback(lambda r: self.assertEquals(r,20))
153
154 t2 = task.MapTask(lambda : 20)
155 d.addCallback(lambda _: self.tc.run(t2))
156 d.addCallback(self.tc.get_task_result, block=True)
157 d.addCallback(lambda r: self.assertEquals(r,20))
158
159 t3 = task.MapTask(lambda x: x,(),{'x':20})
160 d.addCallback(lambda _: self.tc.run(t3))
161 d.addCallback(self.tc.get_task_result, block=True)
162 d.addCallback(lambda r: self.assertEquals(r,20))
163 return d
164
165 def test_map_task_failure(self):
166 self.addEngine(1)
167 t1 = task.MapTask(lambda x: 1/0,(10,))
168 d = self.tc.run(t1)
169 d.addCallback(self.tc.get_task_result, block=True)
170 d.addErrback(lambda f: self.assertRaises(ZeroDivisionError, f.raiseException))
171 return d
172
173 def test_map_task_args(self):
174 self.assertRaises(TypeError, task.MapTask, 'asdfasdf')
175 self.assertRaises(TypeError, task.MapTask, lambda x: x, 10)
176 self.assertRaises(TypeError, task.MapTask, lambda x: x, (10,),30)
177
178 def test_clear(self):
179 self.addEngine(1)
180 t1 = task.MapTask(lambda x: 2*x,(10,))
181 d = self.tc.run(t1)
182 d.addCallback(lambda _: self.tc.get_task_result(0, block=True))
183 d.addCallback(lambda r: self.assertEquals(r,20))
184 d.addCallback(lambda _: self.tc.clear())
185 d.addCallback(lambda _: self.tc.get_task_result(0, block=True))
186 d.addErrback(lambda f: self.assertRaises(IndexError, f.raiseException))
187 return d
@@ -38,7 +38,7 b' try:'
38 IEngineQueuedTestCase
38 IEngineQueuedTestCase
39 except ImportError:
39 except ImportError:
40 print "we got an error!!!"
40 print "we got an error!!!"
41 pass
41 raise
42 else:
42 else:
43 class EngineFCTest(DeferredTestCase,
43 class EngineFCTest(DeferredTestCase,
44 IEngineCoreTestCase,
44 IEngineCoreTestCase,
@@ -26,9 +26,20 b' try:'
26 from IPython.kernel.multienginefc import IFCSynchronousMultiEngine
26 from IPython.kernel.multienginefc import IFCSynchronousMultiEngine
27 from IPython.kernel import multiengine as me
27 from IPython.kernel import multiengine as me
28 from IPython.kernel.clientconnector import ClientConnector
28 from IPython.kernel.clientconnector import ClientConnector
29 from IPython.kernel.parallelfunction import ParallelFunction
30 from IPython.kernel.error import CompositeError
31 from IPython.kernel.util import printer
29 except ImportError:
32 except ImportError:
30 pass
33 pass
31 else:
34 else:
35
36 def _raise_it(f):
37 try:
38 f.raiseException()
39 except CompositeError, e:
40 e.raise_exception()
41
42
32 class FullSynchronousMultiEngineTestCase(DeferredTestCase, IFullSynchronousMultiEngineTestCase):
43 class FullSynchronousMultiEngineTestCase(DeferredTestCase, IFullSynchronousMultiEngineTestCase):
33
44
34 def setUp(self):
45 def setUp(self):
@@ -68,3 +79,66 b' else:'
68 d.addBoth(lambda _: self.controller.stopService())
79 d.addBoth(lambda _: self.controller.stopService())
69 dlist.append(d)
80 dlist.append(d)
70 return defer.DeferredList(dlist)
81 return defer.DeferredList(dlist)
82
83 def test_mapper(self):
84 self.addEngine(4)
85 m = self.multiengine.mapper()
86 self.assertEquals(m.multiengine,self.multiengine)
87 self.assertEquals(m.dist,'b')
88 self.assertEquals(m.targets,'all')
89 self.assertEquals(m.block,True)
90
91 def test_map_default(self):
92 self.addEngine(4)
93 m = self.multiengine.mapper()
94 d = m.map(lambda x: 2*x, range(10))
95 d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)]))
96 d.addCallback(lambda _: self.multiengine.map(lambda x: 2*x, range(10)))
97 d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)]))
98 return d
99
100 def test_map_noblock(self):
101 self.addEngine(4)
102 m = self.multiengine.mapper(block=False)
103 d = m.map(lambda x: 2*x, range(10))
104 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
105 d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)]))
106 return d
107
108 def test_mapper_fail(self):
109 self.addEngine(4)
110 m = self.multiengine.mapper()
111 d = m.map(lambda x: 1/0, range(10))
112 d.addBoth(lambda f: self.assertRaises(ZeroDivisionError, _raise_it, f))
113 return d
114
115 def test_parallel(self):
116 self.addEngine(4)
117 p = self.multiengine.parallel()
118 self.assert_(isinstance(p, ParallelFunction))
119 @p
120 def f(x): return 2*x
121 d = f(range(10))
122 d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)]))
123 return d
124
125 def test_parallel_noblock(self):
126 self.addEngine(1)
127 p = self.multiengine.parallel(block=False)
128 self.assert_(isinstance(p, ParallelFunction))
129 @p
130 def f(x): return 2*x
131 d = f(range(10))
132 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
133 d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)]))
134 return d
135
136 def test_parallel_fail(self):
137 self.addEngine(4)
138 p = self.multiengine.parallel()
139 self.assert_(isinstance(p, ParallelFunction))
140 @p
141 def f(x): return 1/0
142 d = f(range(10))
143 d.addBoth(lambda f: self.assertRaises(ZeroDivisionError, _raise_it, f))
144 return d No newline at end of file
@@ -20,8 +20,6 b' try:'
20 from twisted.internet import defer
20 from twisted.internet import defer
21 from twisted.python import failure
21 from twisted.python import failure
22
22
23 from IPython.testing import tcommon
24 from IPython.testing.tcommon import *
25 from IPython.testing.util import DeferredTestCase
23 from IPython.testing.util import DeferredTestCase
26 import IPython.kernel.pendingdeferred as pd
24 import IPython.kernel.pendingdeferred as pd
27 from IPython.kernel import error
25 from IPython.kernel import error
@@ -29,26 +27,7 b' try:'
29 except ImportError:
27 except ImportError:
30 pass
28 pass
31 else:
29 else:
32
30
33 #-------------------------------------------------------------------------------
34 # Setup for inline and standalone doctests
35 #-------------------------------------------------------------------------------
36
37
38 # If you have standalone doctests in a separate file, set their names in the
39 # dt_files variable (as a single string or a list thereof):
40 dt_files = []
41
42 # If you have any modules whose docstrings should be scanned for embedded tests
43 # as examples accorging to standard doctest practice, set them here (as a
44 # single string or a list thereof):
45 dt_modules = []
46
47 #-------------------------------------------------------------------------------
48 # Regular Unittests
49 #-------------------------------------------------------------------------------
50
51
52 class Foo(object):
31 class Foo(object):
53
32
54 def bar(self, bahz):
33 def bar(self, bahz):
@@ -205,14 +184,3 b' else:'
205 d3 = self.pdm.get_pending_deferred(did,False)
184 d3 = self.pdm.get_pending_deferred(did,False)
206 d3.addCallback(lambda r: self.assertEquals(r,'bar'))
185 d3.addCallback(lambda r: self.assertEquals(r,'bar'))
207
186
208 #-------------------------------------------------------------------------------
209 # Regular Unittests
210 #-------------------------------------------------------------------------------
211
212 # This ensures that the code will run either standalone as a script, or that it
213 # can be picked up by Twisted's `trial` test wrapper to run all the tests.
214 if tcommon.pexpect is not None:
215 if __name__ == '__main__':
216 unittest.main(testLoader=IPDocTestLoader(dt_files,dt_modules))
217 else:
218 testSuite = lambda : makeTestSuite(__name__,dt_files,dt_modules)
@@ -30,6 +30,8 b' try:'
30 from IPython.kernel.util import printer
30 from IPython.kernel.util import printer
31 from IPython.kernel.tests.tasktest import ITaskControllerTestCase
31 from IPython.kernel.tests.tasktest import ITaskControllerTestCase
32 from IPython.kernel.clientconnector import ClientConnector
32 from IPython.kernel.clientconnector import ClientConnector
33 from IPython.kernel.error import CompositeError
34 from IPython.kernel.parallelfunction import ParallelFunction
33 except ImportError:
35 except ImportError:
34 pass
36 pass
35 else:
37 else:
@@ -38,6 +40,12 b' else:'
38 # Tests
40 # Tests
39 #-------------------------------------------------------------------------------
41 #-------------------------------------------------------------------------------
40
42
43 def _raise_it(f):
44 try:
45 f.raiseException()
46 except CompositeError, e:
47 e.raise_exception()
48
41 class TaskTest(DeferredTestCase, ITaskControllerTestCase):
49 class TaskTest(DeferredTestCase, ITaskControllerTestCase):
42
50
43 def setUp(self):
51 def setUp(self):
@@ -87,4 +95,67 b' else:'
87 d.addBoth(lambda _: self.controller.stopService())
95 d.addBoth(lambda _: self.controller.stopService())
88 dlist.append(d)
96 dlist.append(d)
89 return defer.DeferredList(dlist)
97 return defer.DeferredList(dlist)
90
98
99 def test_mapper(self):
100 self.addEngine(1)
101 m = self.tc.mapper()
102 self.assertEquals(m.task_controller,self.tc)
103 self.assertEquals(m.clear_before,False)
104 self.assertEquals(m.clear_after,False)
105 self.assertEquals(m.retries,0)
106 self.assertEquals(m.recovery_task,None)
107 self.assertEquals(m.depend,None)
108 self.assertEquals(m.block,True)
109
110 def test_map_default(self):
111 self.addEngine(1)
112 m = self.tc.mapper()
113 d = m.map(lambda x: 2*x, range(10))
114 d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)]))
115 d.addCallback(lambda _: self.tc.map(lambda x: 2*x, range(10)))
116 d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)]))
117 return d
118
119 def test_map_noblock(self):
120 self.addEngine(1)
121 m = self.tc.mapper(block=False)
122 d = m.map(lambda x: 2*x, range(10))
123 d.addCallback(lambda r: self.assertEquals(r,[x for x in range(10)]))
124 return d
125
126 def test_mapper_fail(self):
127 self.addEngine(1)
128 m = self.tc.mapper()
129 d = m.map(lambda x: 1/0, range(10))
130 d.addBoth(lambda f: self.assertRaises(ZeroDivisionError, _raise_it, f))
131 return d
132
133 def test_parallel(self):
134 self.addEngine(1)
135 p = self.tc.parallel()
136 self.assert_(isinstance(p, ParallelFunction))
137 @p
138 def f(x): return 2*x
139 d = f(range(10))
140 d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)]))
141 return d
142
143 def test_parallel_noblock(self):
144 self.addEngine(1)
145 p = self.tc.parallel(block=False)
146 self.assert_(isinstance(p, ParallelFunction))
147 @p
148 def f(x): return 2*x
149 d = f(range(10))
150 d.addCallback(lambda r: self.assertEquals(r,[x for x in range(10)]))
151 return d
152
153 def test_parallel_fail(self):
154 self.addEngine(1)
155 p = self.tc.parallel()
156 self.assert_(isinstance(p, ParallelFunction))
157 @p
158 def f(x): return 1/0
159 d = f(range(10))
160 d.addBoth(lambda f: self.assertRaises(ZeroDivisionError, _raise_it, f))
161 return d No newline at end of file
@@ -1,13 +1,32 b''
1 """String dispatch class to match regexps and dispatch commands.
2 """
3
4 # Stdlib imports
5 import re
6
7 # Our own modules
1 from IPython.hooks import CommandChainDispatcher
8 from IPython.hooks import CommandChainDispatcher
2 import IPython.hooks
9 import IPython.hooks
3
10
4 import re
5
11
12 # Code begins
6 class StrDispatch(object):
13 class StrDispatch(object):
7 """ Dispatch (lookup) a set of strings / regexps for match """
14 """Dispatch (lookup) a set of strings / regexps for match.
15
16 Example:
17
18 >>> dis = StrDispatch()
19 >>> dis.add_s('hei',34, priority = 4)
20 >>> dis.add_s('hei',123, priority = 2)
21 >>> dis.add_re('h.i', 686)
22 >>> print list(dis.flat_matches('hei'))
23 [123, 34, 686]
24 """
25
8 def __init__(self):
26 def __init__(self):
9 self.strs = {}
27 self.strs = {}
10 self.regexs = {}
28 self.regexs = {}
29
11 def add_s(self, s, obj, priority= 0 ):
30 def add_s(self, s, obj, priority= 0 ):
12 """ Adds a target 'string' for dispatching """
31 """ Adds a target 'string' for dispatching """
13
32
@@ -31,9 +50,8 b' class StrDispatch(object):'
31 if re.match(r, key):
50 if re.match(r, key):
32 yield obj
51 yield obj
33 else:
52 else:
34 #print "nomatch",key
53 #print "nomatch",key # dbg
35 pass
54 pass
36
37
55
38 def __repr__(self):
56 def __repr__(self):
39 return "<Strdispatch %s, %s>" % (self.strs, self.regexs)
57 return "<Strdispatch %s, %s>" % (self.strs, self.regexs)
@@ -44,22 +62,9 b' class StrDispatch(object):'
44 for el in self.strs[key]:
62 for el in self.strs[key]:
45 yield el[1]
63 yield el[1]
46
64
47
48 def flat_matches(self, key):
65 def flat_matches(self, key):
49 """ Yield all 'value' targets, without priority """
66 """ Yield all 'value' targets, without priority """
50 for val in self.dispatch(key):
67 for val in self.dispatch(key):
51 for el in val:
68 for el in val:
52 yield el[1] # only value, no priority
69 yield el[1] # only value, no priority
53 return
70 return
54
55
56 def test():
57 d = StrDispatch()
58 d.add_s('hei',34, priority = 4)
59 d.add_s('hei',123, priority = 2)
60 print list(d.dispatch('hei'))
61 d.add_re('h.i', 686)
62 print list(d.flat_matches('hei'))
63
64 if __name__ == '__main__':
65 test() No newline at end of file
1 NO CONTENT: file renamed from IPython/testing/attic/parametric.py to IPython/testing/parametric.py
NO CONTENT: file renamed from IPython/testing/attic/parametric.py to IPython/testing/parametric.py
@@ -2,17 +2,51 b''
2 PREFIX=~/usr/local
2 PREFIX=~/usr/local
3 PREFIX=~/tmp/local
3 PREFIX=~/tmp/local
4
4
5 NOSE0=nosetests -vs --with-doctest --doctest-tests
6 NOSE=nosetests -vvs --with-ipdoctest --doctest-tests --doctest-extension=txt
7
8 #--with-color
9
10 SRC=ipdoctest.py setup.py ../decorators.py
11
5 plugin: IPython_doctest_plugin.egg-info
12 plugin: IPython_doctest_plugin.egg-info
6
13
14 dtest: plugin dtexample.py
15 $(NOSE) dtexample.py
16
17 # Note: this test is double counting!!!
18 rtest: plugin dtexample.py
19 $(NOSE) test_refs.py
20
21 std: plugin
22 nosetests -vs --with-doctest --doctest-tests IPython.strdispatch
23 $(NOSE) IPython.strdispatch
24
7 test: plugin dtexample.py
25 test: plugin dtexample.py
8 nosetests -s --with-ipdoctest --doctest-tests --doctest-extension=txt \
26 $(NOSE) dtexample.py test*.py test*.txt
9 dtexample.py test*.txt
10
27
11 deb: plugin dtexample.py
28 deb: plugin dtexample.py
12 nosetests -vs --with-ipdoctest --doctest-tests --doctest-extension=txt \
29 $(NOSE) test_combo.txt
13 test_combo.txt
30
31 iptest: plugin
32 $(NOSE) IPython
33
34 deco:
35 $(NOSE0) IPython.testing.decorators
36
37 mtest: plugin
38 $(NOSE) -x IPython.Magic
39
40 ipipe: plugin
41 $(NOSE) -x IPython.Extensions.ipipe
42
43 sr: rtest std
44
45 base: dtest rtest test std deco
46
47 all: base iptest
14
48
15 IPython_doctest_plugin.egg-info: ipdoctest.py setup.py
49 IPython_doctest_plugin.egg-info: $(SRC)
16 python setup.py install --prefix=$(PREFIX)
50 python setup.py install --prefix=$(PREFIX)
17 touch $@
51 touch $@
18
52
@@ -21,9 +21,9 b' def pyfunc():'
21 ...
21 ...
22 0 1 1 2 2 3
22 0 1 1 2 2 3
23 """
23 """
24
25 return 'pyfunc'
24 return 'pyfunc'
26
25
26
27 def ipfunc():
27 def ipfunc():
28 """Some ipython tests...
28 """Some ipython tests...
29
29
@@ -67,6 +67,93 b' def ipfunc():'
67 In [9]: ipfunc()
67 In [9]: ipfunc()
68 Out[9]: 'ipfunc'
68 Out[9]: 'ipfunc'
69 """
69 """
70
71 return 'ipfunc'
70 return 'ipfunc'
72
71
72
73 def ranfunc():
74 """A function with some random output.
75
76 Normal examples are verified as usual:
77 >>> 1+3
78 4
79
80 But if you put '# random' in the output, it is ignored:
81 >>> 1+3
82 junk goes here... # random
83
84 >>> 1+2
85 again, anything goes #random
86 if multiline, the random mark is only needed once.
87
88 >>> 1+2
89 You can also put the random marker at the end:
90 # random
91
92 >>> 1+2
93 # random
94 .. or at the beginning.
95
96 More correct input is properly verified:
97 >>> ranfunc()
98 'ranfunc'
99 """
100 return 'ranfunc'
101
102
103 def random_all():
104 """A function where we ignore the output of ALL examples.
105
106 Examples:
107
108 # all-random
109
110 This mark tells the testing machinery that all subsequent examples should
111 be treated as random (ignoring their output). They are still executed,
112 so if a they raise an error, it will be detected as such, but their
113 output is completely ignored.
114
115 >>> 1+3
116 junk goes here...
117
118 >>> 1+3
119 klasdfj;
120
121 >>> 1+2
122 again, anything goes
123 blah...
124 """
125 pass
126
127
128 def iprand():
129 """Some ipython tests with random output.
130
131 In [7]: 3+4
132 Out[7]: 7
133
134 In [8]: print 'hello'
135 world # random
136
137 In [9]: iprand()
138 Out[9]: 'iprand'
139 """
140 return 'iprand'
141
142
143 def iprand_all():
144 """Some ipython tests with fully random output.
145
146 # all-random
147
148 In [7]: 1
149 Out[7]: 99
150
151 In [8]: print 'hello'
152 world
153
154 In [9]: iprand_all()
155 Out[9]: 'junk'
156 """
157 return 'iprand_all'
158
159
@@ -43,9 +43,19 b' import logging'
43 import os
43 import os
44 import re
44 import re
45 import sys
45 import sys
46 import traceback
46 import unittest
47 import unittest
47
48
48 from inspect import getmodule
49 from inspect import getmodule
50 from StringIO import StringIO
51
52 # We are overriding the default doctest runner, so we need to import a few
53 # things from doctest directly
54 from doctest import (REPORTING_FLAGS, REPORT_ONLY_FIRST_FAILURE,
55 _unittest_reportflags, DocTestRunner,
56 _extract_future_flags, pdb, _OutputRedirectingPdb,
57 _exception_traceback,
58 linecache)
49
59
50 # Third-party modules
60 # Third-party modules
51 import nose.core
61 import nose.core
@@ -68,9 +78,27 b' log = logging.getLogger(__name__)'
68 # machinery into a fit. This code should be considered a gross hack, but it
78 # machinery into a fit. This code should be considered a gross hack, but it
69 # gets the job done.
79 # gets the job done.
70
80
81
82 # XXX - Hack to modify the %run command so we can sync the user's namespace
83 # with the test globals. Once we move over to a clean magic system, this will
84 # be done with much less ugliness.
85
86 def _run_ns_sync(self,arg_s,runner=None):
87 """Modified version of %run that syncs testing namespaces.
88
89 This is strictly needed for running doctests that call %run.
90 """
91
92 out = _ip.IP.magic_run_ori(arg_s,runner)
93 _run_ns_sync.test_globs.update(_ip.user_ns)
94 return out
95
96
71 def start_ipython():
97 def start_ipython():
72 """Start a global IPython shell, which we need for IPython-specific syntax.
98 """Start a global IPython shell, which we need for IPython-specific syntax.
73 """
99 """
100 import new
101
74 import IPython
102 import IPython
75
103
76 def xsys(cmd):
104 def xsys(cmd):
@@ -88,7 +116,7 b' def start_ipython():'
88 _excepthook = sys.excepthook
116 _excepthook = sys.excepthook
89 _main = sys.modules.get('__main__')
117 _main = sys.modules.get('__main__')
90
118
91 # Start IPython instance
119 # Start IPython instance. We customize it to start with minimal frills.
92 IPython.Shell.IPShell(['--classic','--noterm_title'])
120 IPython.Shell.IPShell(['--classic','--noterm_title'])
93
121
94 # Deactivate the various python system hooks added by ipython for
122 # Deactivate the various python system hooks added by ipython for
@@ -107,6 +135,11 b' def start_ipython():'
107 # doctest machinery would miss them.
135 # doctest machinery would miss them.
108 _ip.system = xsys
136 _ip.system = xsys
109
137
138 # Also patch our %run function in.
139 im = new.instancemethod(_run_ns_sync,_ip.IP, _ip.IP.__class__)
140 _ip.IP.magic_run_ori = _ip.IP.magic_run
141 _ip.IP.magic_run = im
142
110 # The start call MUST be made here. I'm not sure yet why it doesn't work if
143 # The start call MUST be made here. I'm not sure yet why it doesn't work if
111 # it is made later, at plugin initialization time, but in all my tests, that's
144 # it is made later, at plugin initialization time, but in all my tests, that's
112 # the case.
145 # the case.
@@ -115,7 +148,26 b' start_ipython()'
115 # *** END HACK ***
148 # *** END HACK ***
116 ###########################################################################
149 ###########################################################################
117
150
118 #-----------------------------------------------------------------------------
151 # Classes and functions
152
153 def is_extension_module(filename):
154 """Return whether the given filename is an extension module.
155
156 This simply checks that the extension is either .so or .pyd.
157 """
158 return os.path.splitext(filename)[1].lower() in ('.so','.pyd')
159
160
161 class nodoc(object):
162 def __init__(self,obj):
163 self.obj = obj
164
165 def __getattribute__(self,key):
166 if key == '__doc__':
167 return None
168 else:
169 return getattr(object.__getattribute__(self,'obj'),key)
170
119 # Modified version of the one in the stdlib, that fixes a python bug (doctests
171 # Modified version of the one in the stdlib, that fixes a python bug (doctests
120 # not found in extension modules, http://bugs.python.org/issue3158)
172 # not found in extension modules, http://bugs.python.org/issue3158)
121 class DocTestFinder(doctest.DocTestFinder):
173 class DocTestFinder(doctest.DocTestFinder):
@@ -126,45 +178,38 b' class DocTestFinder(doctest.DocTestFinder):'
126 module.
178 module.
127 """
179 """
128 if module is None:
180 if module is None:
129 #print '_fm C1' # dbg
130 return True
181 return True
131 elif inspect.isfunction(object):
182 elif inspect.isfunction(object):
132 #print '_fm C2' # dbg
133 return module.__dict__ is object.func_globals
183 return module.__dict__ is object.func_globals
134 elif inspect.isbuiltin(object):
184 elif inspect.isbuiltin(object):
135 #print '_fm C2-1' # dbg
136 return module.__name__ == object.__module__
185 return module.__name__ == object.__module__
137 elif inspect.isclass(object):
186 elif inspect.isclass(object):
138 #print '_fm C3' # dbg
139 return module.__name__ == object.__module__
187 return module.__name__ == object.__module__
140 elif inspect.ismethod(object):
188 elif inspect.ismethod(object):
141 # This one may be a bug in cython that fails to correctly set the
189 # This one may be a bug in cython that fails to correctly set the
142 # __module__ attribute of methods, but since the same error is easy
190 # __module__ attribute of methods, but since the same error is easy
143 # to make by extension code writers, having this safety in place
191 # to make by extension code writers, having this safety in place
144 # isn't such a bad idea
192 # isn't such a bad idea
145 #print '_fm C3-1' # dbg
146 return module.__name__ == object.im_class.__module__
193 return module.__name__ == object.im_class.__module__
147 elif inspect.getmodule(object) is not None:
194 elif inspect.getmodule(object) is not None:
148 #print '_fm C4' # dbg
149 #print 'C4 mod',module,'obj',object # dbg
150 return module is inspect.getmodule(object)
195 return module is inspect.getmodule(object)
151 elif hasattr(object, '__module__'):
196 elif hasattr(object, '__module__'):
152 #print '_fm C5' # dbg
153 return module.__name__ == object.__module__
197 return module.__name__ == object.__module__
154 elif isinstance(object, property):
198 elif isinstance(object, property):
155 #print '_fm C6' # dbg
156 return True # [XX] no way not be sure.
199 return True # [XX] no way not be sure.
157 else:
200 else:
158 raise ValueError("object must be a class or function")
201 raise ValueError("object must be a class or function")
159
202
160
161
162 def _find(self, tests, obj, name, module, source_lines, globs, seen):
203 def _find(self, tests, obj, name, module, source_lines, globs, seen):
163 """
204 """
164 Find tests for the given object and any contained objects, and
205 Find tests for the given object and any contained objects, and
165 add them to `tests`.
206 add them to `tests`.
166 """
207 """
167
208
209 if hasattr(obj,"skip_doctest"):
210 #print 'SKIPPING DOCTEST FOR:',obj # dbg
211 obj = nodoc(obj)
212
168 doctest.DocTestFinder._find(self,tests, obj, name, module,
213 doctest.DocTestFinder._find(self,tests, obj, name, module,
169 source_lines, globs, seen)
214 source_lines, globs, seen)
170
215
@@ -185,13 +230,10 b' class DocTestFinder(doctest.DocTestFinder):'
185 self._find(tests, val, valname1, module, source_lines,
230 self._find(tests, val, valname1, module, source_lines,
186 globs, seen)
231 globs, seen)
187
232
188
189 # Look for tests in a class's contained objects.
233 # Look for tests in a class's contained objects.
190 if inspect.isclass(obj) and self._recurse:
234 if inspect.isclass(obj) and self._recurse:
191 #print 'RECURSE into class:',obj # dbg
235 #print 'RECURSE into class:',obj # dbg
192 for valname, val in obj.__dict__.items():
236 for valname, val in obj.__dict__.items():
193 #valname1 = '%s.%s' % (name, valname) # dbg
194 #print 'N',name,'VN:',valname,'val:',str(val)[:77] # dbg
195 # Special handling for staticmethod/classmethod.
237 # Special handling for staticmethod/classmethod.
196 if isinstance(val, staticmethod):
238 if isinstance(val, staticmethod):
197 val = getattr(obj, valname)
239 val = getattr(obj, valname)
@@ -208,6 +250,32 b' class DocTestFinder(doctest.DocTestFinder):'
208 globs, seen)
250 globs, seen)
209
251
210
252
253 class IPDoctestOutputChecker(doctest.OutputChecker):
254 """Second-chance checker with support for random tests.
255
256 If the default comparison doesn't pass, this checker looks in the expected
257 output string for flags that tell us to ignore the output.
258 """
259
260 random_re = re.compile(r'#\s*random')
261
262 def check_output(self, want, got, optionflags):
263 """Check output, accepting special markers embedded in the output.
264
265 If the output didn't pass the default validation but the special string
266 '#random' is included, we accept it."""
267
268 # Let the original tester verify first, in case people have valid tests
269 # that happen to have a comment saying '#random' embedded in.
270 ret = doctest.OutputChecker.check_output(self, want, got,
271 optionflags)
272 if not ret and self.random_re.search(want):
273 #print >> sys.stderr, 'RANDOM OK:',want # dbg
274 return True
275
276 return ret
277
278
211 class DocTestCase(doctests.DocTestCase):
279 class DocTestCase(doctests.DocTestCase):
212 """Proxy for DocTestCase: provides an address() method that
280 """Proxy for DocTestCase: provides an address() method that
213 returns the correct address for the doctest case. Otherwise
281 returns the correct address for the doctest case. Otherwise
@@ -216,33 +284,70 b' class DocTestCase(doctests.DocTestCase):'
216 for purposes of determining the test address, if it is provided.
284 for purposes of determining the test address, if it is provided.
217 """
285 """
218
286
219 # doctests loaded via find(obj) omit the module name
287 # Note: this method was taken from numpy's nosetester module.
220 # so we need to override id, __repr__ and shortDescription
288
221 # bonus: this will squash a 2.3 vs 2.4 incompatiblity
289 # Subclass nose.plugins.doctests.DocTestCase to work around a bug in
222 def id(self):
290 # its constructor that blocks non-default arguments from being passed
223 name = self._dt_test.name
291 # down into doctest.DocTestCase
224 filename = self._dt_test.filename
292
225 if filename is not None:
293 def __init__(self, test, optionflags=0, setUp=None, tearDown=None,
226 pk = getpackage(filename)
294 checker=None, obj=None, result_var='_'):
227 if pk is not None and not name.startswith(pk):
295 self._result_var = result_var
228 name = "%s.%s" % (pk, name)
296 doctests.DocTestCase.__init__(self, test,
229 return name
297 optionflags=optionflags,
298 setUp=setUp, tearDown=tearDown,
299 checker=checker)
300 # Now we must actually copy the original constructor from the stdlib
301 # doctest class, because we can't call it directly and a bug in nose
302 # means it never gets passed the right arguments.
303
304 self._dt_optionflags = optionflags
305 self._dt_checker = checker
306 self._dt_test = test
307 self._dt_setUp = setUp
308 self._dt_tearDown = tearDown
309
310 # Each doctest should remember what directory it was loaded from...
311 self._ori_dir = os.getcwd()
312
313 # Modified runTest from the default stdlib
314 def runTest(self):
315 test = self._dt_test
316 old = sys.stdout
317 new = StringIO()
318 optionflags = self._dt_optionflags
319
320 if not (optionflags & REPORTING_FLAGS):
321 # The option flags don't include any reporting flags,
322 # so add the default reporting flags
323 optionflags |= _unittest_reportflags
324
325 runner = IPDocTestRunner(optionflags=optionflags,
326 checker=self._dt_checker, verbose=False)
230
327
328 try:
329 # Save our current directory and switch out to the one where the
330 # test was originally created, in case another doctest did a
331 # directory change. We'll restore this in the finally clause.
332 curdir = os.getcwd()
333 os.chdir(self._ori_dir)
334
335 runner.DIVIDER = "-"*70
336 failures, tries = runner.run(
337 test, out=new.write, clear_globs=False)
338 finally:
339 sys.stdout = old
340 os.chdir(curdir)
231
341
232 # Classes and functions
342 if failures:
233
343 raise self.failureException(self.format_failure(new.getvalue()))
234 def is_extension_module(filename):
235 """Return whether the given filename is an extension module.
236
237 This simply checks that the extension is either .so or .pyd.
238 """
239 return os.path.splitext(filename)[1].lower() in ('.so','.pyd')
240
344
241
345
242 # A simple subclassing of the original with a different class name, so we can
346 # A simple subclassing of the original with a different class name, so we can
243 # distinguish and treat differently IPython examples from pure python ones.
347 # distinguish and treat differently IPython examples from pure python ones.
244 class IPExample(doctest.Example): pass
348 class IPExample(doctest.Example): pass
245
349
350
246 class IPExternalExample(doctest.Example):
351 class IPExternalExample(doctest.Example):
247 """Doctest examples to be run in an external process."""
352 """Doctest examples to be run in an external process."""
248
353
@@ -254,6 +359,7 b' class IPExternalExample(doctest.Example):'
254 # An EXTRA newline is needed to prevent pexpect hangs
359 # An EXTRA newline is needed to prevent pexpect hangs
255 self.source += '\n'
360 self.source += '\n'
256
361
362
257 class IPDocTestParser(doctest.DocTestParser):
363 class IPDocTestParser(doctest.DocTestParser):
258 """
364 """
259 A class used to parse strings containing doctest examples.
365 A class used to parse strings containing doctest examples.
@@ -294,14 +400,22 b' class IPDocTestParser(doctest.DocTestParser):'
294 _EXAMPLE_RE_IP = re.compile( _RE_TPL % (_PS1_IP,_PS2_IP,_PS1_IP,_PS2_IP),
400 _EXAMPLE_RE_IP = re.compile( _RE_TPL % (_PS1_IP,_PS2_IP,_PS1_IP,_PS2_IP),
295 re.MULTILINE | re.VERBOSE)
401 re.MULTILINE | re.VERBOSE)
296
402
403 # Mark a test as being fully random. In this case, we simply append the
404 # random marker ('#random') to each individual example's output. This way
405 # we don't need to modify any other code.
406 _RANDOM_TEST = re.compile(r'#\s*all-random')
407
408 # Mark tests to be executed in an external process - currently unsupported.
409 _EXTERNAL_IP = re.compile(r'#\s*ipdoctest:\s*EXTERNAL')
410
297 def ip2py(self,source):
411 def ip2py(self,source):
298 """Convert input IPython source into valid Python."""
412 """Convert input IPython source into valid Python."""
299 out = []
413 out = []
300 newline = out.append
414 newline = out.append
301 for line in source.splitlines():
415 for lnum,line in enumerate(source.splitlines()):
302 #newline(_ip.IPipython.prefilter(line,True))
416 newline(_ip.IP.prefilter(line,lnum>0))
303 newline(_ip.IP.prefilter(line,True))
304 newline('') # ensure a closing newline, needed by doctest
417 newline('') # ensure a closing newline, needed by doctest
418 #print "PYSRC:", '\n'.join(out) # dbg
305 return '\n'.join(out)
419 return '\n'.join(out)
306
420
307 def parse(self, string, name='<string>'):
421 def parse(self, string, name='<string>'):
@@ -324,6 +438,11 b' class IPDocTestParser(doctest.DocTestParser):'
324 output = []
438 output = []
325 charno, lineno = 0, 0
439 charno, lineno = 0, 0
326
440
441 if self._RANDOM_TEST.search(string):
442 random_marker = '\n# random'
443 else:
444 random_marker = ''
445
327 # Whether to convert the input from ipython to python syntax
446 # Whether to convert the input from ipython to python syntax
328 ip2py = False
447 ip2py = False
329 # Find all doctest examples in the string. First, try them as Python
448 # Find all doctest examples in the string. First, try them as Python
@@ -341,7 +460,7 b' class IPDocTestParser(doctest.DocTestParser):'
341 # IPExternalExamples are run out-of-process (via pexpect) so they
460 # IPExternalExamples are run out-of-process (via pexpect) so they
342 # don't need any filtering (a real ipython will be executing them).
461 # don't need any filtering (a real ipython will be executing them).
343 terms = list(self._EXAMPLE_RE_IP.finditer(string))
462 terms = list(self._EXAMPLE_RE_IP.finditer(string))
344 if re.search(r'#\s*ipdoctest:\s*EXTERNAL',string):
463 if self._EXTERNAL_IP.search(string):
345 #print '-'*70 # dbg
464 #print '-'*70 # dbg
346 #print 'IPExternalExample, Source:\n',string # dbg
465 #print 'IPExternalExample, Source:\n',string # dbg
347 #print '-'*70 # dbg
466 #print '-'*70 # dbg
@@ -361,12 +480,17 b' class IPDocTestParser(doctest.DocTestParser):'
361 # Extract info from the regexp match.
480 # Extract info from the regexp match.
362 (source, options, want, exc_msg) = \
481 (source, options, want, exc_msg) = \
363 self._parse_example(m, name, lineno,ip2py)
482 self._parse_example(m, name, lineno,ip2py)
483
484 # Append the random-output marker (it defaults to empty in most
485 # cases, it's only non-empty for 'all-random' tests):
486 want += random_marker
487
364 if Example is IPExternalExample:
488 if Example is IPExternalExample:
365 options[doctest.NORMALIZE_WHITESPACE] = True
489 options[doctest.NORMALIZE_WHITESPACE] = True
366 want += '\n'
490 want += '\n'
491
367 # Create an Example, and add it to the list.
492 # Create an Example, and add it to the list.
368 if not self._IS_BLANK_OR_COMMENT(source):
493 if not self._IS_BLANK_OR_COMMENT(source):
369 #print 'Example source:', source # dbg
370 output.append(Example(source, want, exc_msg,
494 output.append(Example(source, want, exc_msg,
371 lineno=lineno,
495 lineno=lineno,
372 indent=min_indent+len(m.group('indent')),
496 indent=min_indent+len(m.group('indent')),
@@ -377,7 +501,6 b' class IPDocTestParser(doctest.DocTestParser):'
377 charno = m.end()
501 charno = m.end()
378 # Add any remaining post-example text to `output`.
502 # Add any remaining post-example text to `output`.
379 output.append(string[charno:])
503 output.append(string[charno:])
380
381 return output
504 return output
382
505
383 def _parse_example(self, m, name, lineno,ip2py=False):
506 def _parse_example(self, m, name, lineno,ip2py=False):
@@ -464,9 +587,33 b' class IPDocTestParser(doctest.DocTestParser):'
464 (lineno+i+1, name,
587 (lineno+i+1, name,
465 line[indent:space_idx], line))
588 line[indent:space_idx], line))
466
589
590
467 SKIP = doctest.register_optionflag('SKIP')
591 SKIP = doctest.register_optionflag('SKIP')
468
592
469 ###########################################################################
593
594 class IPDocTestRunner(doctest.DocTestRunner,object):
595 """Test runner that synchronizes the IPython namespace with test globals.
596 """
597
598 def run(self, test, compileflags=None, out=None, clear_globs=True):
599
600 # Hack: ipython needs access to the execution context of the example,
601 # so that it can propagate user variables loaded by %run into
602 # test.globs. We put them here into our modified %run as a function
603 # attribute. Our new %run will then only make the namespace update
604 # when called (rather than unconconditionally updating test.globs here
605 # for all examples, most of which won't be calling %run anyway).
606 _run_ns_sync.test_globs = test.globs
607
608 # dbg
609 ## print >> sys.stderr, "Test:",test
610 ## for ex in test.examples:
611 ## print >> sys.stderr, ex.source
612 ## print >> sys.stderr, 'Want:\n',ex.want,'\n--'
613
614 return super(IPDocTestRunner,self).run(test,
615 compileflags,out,clear_globs)
616
470
617
471 class DocFileCase(doctest.DocFileCase):
618 class DocFileCase(doctest.DocFileCase):
472 """Overrides to provide filename
619 """Overrides to provide filename
@@ -490,7 +637,8 b' class ExtensionDoctest(doctests.Doctest):'
490 self.extension = tolist(options.doctestExtension)
637 self.extension = tolist(options.doctestExtension)
491 self.finder = DocTestFinder()
638 self.finder = DocTestFinder()
492 self.parser = doctest.DocTestParser()
639 self.parser = doctest.DocTestParser()
493
640 self.globs = None
641 self.extraglobs = None
494
642
495 def loadTestsFromExtensionModule(self,filename):
643 def loadTestsFromExtensionModule(self,filename):
496 bpath,mod = os.path.split(filename)
644 bpath,mod = os.path.split(filename)
@@ -503,32 +651,62 b' class ExtensionDoctest(doctests.Doctest):'
503 sys.path.pop()
651 sys.path.pop()
504 return tests
652 return tests
505
653
654 # NOTE: the method below is almost a copy of the original one in nose, with
655 # a few modifications to control output checking.
656
657 def loadTestsFromModule(self, module):
658 #print 'lTM',module # dbg
659
660 if not self.matches(module.__name__):
661 log.debug("Doctest doesn't want module %s", module)
662 return
663
664 tests = self.finder.find(module,globs=self.globs,
665 extraglobs=self.extraglobs)
666 if not tests:
667 return
668
669 tests.sort()
670 module_file = module.__file__
671 if module_file[-4:] in ('.pyc', '.pyo'):
672 module_file = module_file[:-1]
673 for test in tests:
674 if not test.examples:
675 continue
676 if not test.filename:
677 test.filename = module_file
678
679 # xxx - checker and options may be ok instantiated once outside loop
680 # always use whitespace and ellipsis options
681 optionflags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
682 checker = IPDoctestOutputChecker()
683
684 yield DocTestCase(test,
685 optionflags=optionflags,
686 checker=checker)
687
506 def loadTestsFromFile(self, filename):
688 def loadTestsFromFile(self, filename):
689 #print 'lTF',filename # dbg
690
507 if is_extension_module(filename):
691 if is_extension_module(filename):
508 for t in self.loadTestsFromExtensionModule(filename):
692 for t in self.loadTestsFromExtensionModule(filename):
509 yield t
693 yield t
510 else:
694 else:
511 ## for t in list(doctests.Doctest.loadTestsFromFile(self,filename)):
695 if self.extension and anyp(filename.endswith, self.extension):
512 ## yield t
696 name = os.path.basename(filename)
513 pass
697 dh = open(filename)
514
698 try:
515 if self.extension and anyp(filename.endswith, self.extension):
699 doc = dh.read()
516 #print 'lTF',filename # dbg
700 finally:
517 name = os.path.basename(filename)
701 dh.close()
518 dh = open(filename)
702 test = self.parser.get_doctest(
519 try:
703 doc, globs={'__file__': filename}, name=name,
520 doc = dh.read()
704 filename=filename, lineno=0)
521 finally:
705 if test.examples:
522 dh.close()
706 #print 'FileCase:',test.examples # dbg
523 test = self.parser.get_doctest(
707 yield DocFileCase(test)
524 doc, globs={'__file__': filename}, name=name,
708 else:
525 filename=filename, lineno=0)
709 yield False # no tests to load
526 if test.examples:
527 #print 'FileCase:',test.examples # dbg
528 yield DocFileCase(test)
529 else:
530 yield False # no tests to load
531
532
710
533 def wantFile(self,filename):
711 def wantFile(self,filename):
534 """Return whether the given filename should be scanned for tests.
712 """Return whether the given filename should be scanned for tests.
@@ -538,38 +716,25 b' class ExtensionDoctest(doctests.Doctest):'
538 """
716 """
539 #print 'Filename:',filename # dbg
717 #print 'Filename:',filename # dbg
540
718
719 # temporarily hardcoded list, will move to driver later
720 exclude = ['IPython/external/',
721 'IPython/Extensions/ipy_',
722 'IPython/platutils_win32',
723 'IPython/frontend/cocoa',
724 'IPython_doctest_plugin',
725 'IPython/Gnuplot',
726 'IPython/Extensions/PhysicalQIn']
727
728 for fex in exclude:
729 if fex in filename: # substring
730 #print '###>>> SKIP:',filename # dbg
731 return False
732
541 if is_extension_module(filename):
733 if is_extension_module(filename):
542 return True
734 return True
543 else:
735 else:
544 return doctests.Doctest.wantFile(self,filename)
736 return doctests.Doctest.wantFile(self,filename)
545
737
546 # NOTE: the method below is a *copy* of the one in the nose doctests
547 # plugin, but we have to replicate it here in order to have it resolve the
548 # DocTestCase (last line) to our local copy, since the nose plugin doesn't
549 # provide a public hook for what TestCase class to use. The alternative
550 # would be to monkeypatch doctest in the stdlib, but that's ugly and
551 # brittle, since a change in plugin load order can break it. So for now,
552 # we just paste this in here, inelegant as this may be.
553
554 def loadTestsFromModule(self, module):
555 #print 'lTM',module # dbg
556
557 if not self.matches(module.__name__):
558 log.debug("Doctest doesn't want module %s", module)
559 return
560 tests = self.finder.find(module)
561 if not tests:
562 return
563 tests.sort()
564 module_file = module.__file__
565 if module_file[-4:] in ('.pyc', '.pyo'):
566 module_file = module_file[:-1]
567 for test in tests:
568 if not test.examples:
569 continue
570 if not test.filename:
571 test.filename = module_file
572 yield DocTestCase(test)
573
738
574 class IPythonDoctest(ExtensionDoctest):
739 class IPythonDoctest(ExtensionDoctest):
575 """Nose Plugin that supports doctests in extension modules.
740 """Nose Plugin that supports doctests in extension modules.
@@ -583,5 +748,6 b' class IPythonDoctest(ExtensionDoctest):'
583 self.doctest_tests = options.doctest_tests
748 self.doctest_tests = options.doctest_tests
584 self.extension = tolist(options.doctestExtension)
749 self.extension = tolist(options.doctestExtension)
585 self.parser = IPDocTestParser()
750 self.parser = IPDocTestParser()
586 #self.finder = DocTestFinder(parser=IPDocTestParser())
587 self.finder = DocTestFinder(parser=self.parser)
751 self.finder = DocTestFinder(parser=self.parser)
752 self.globs = None
753 self.extraglobs = None
@@ -53,7 +53,7 b' class DistributedSpider(object):'
53 self.allLinks.append(url)
53 self.allLinks.append(url)
54 if url.startswith(self.site):
54 if url.startswith(self.site):
55 print ' ', url
55 print ' ', url
56 self.linksWorking[url] = self.tc.run(client.Task('links = fetchAndParse(url)', pull=['links'], push={'url': url}))
56 self.linksWorking[url] = self.tc.run(client.StringTask('links = fetchAndParse(url)', pull=['links'], push={'url': url}))
57
57
58 def onVisitDone(self, result, url):
58 def onVisitDone(self, result, url):
59 print url, ':'
59 print url, ':'
@@ -8,7 +8,7 b' tc = client.TaskClient()'
8 mec = client.MultiEngineClient()
8 mec = client.MultiEngineClient()
9
9
10 mec.execute('import time')
10 mec.execute('import time')
11 hello_taskid = tc.run(client.Task('time.sleep(3) ; word = "Hello,"', pull=('word')))
11 hello_taskid = tc.run(client.StringTask('time.sleep(3) ; word = "Hello,"', pull=('word')))
12 world_taskid = tc.run(client.Task('time.sleep(3) ; word = "World!"', pull=('word')))
12 world_taskid = tc.run(client.StringTask('time.sleep(3) ; word = "World!"', pull=('word')))
13 print "Submitted tasks:", hello_taskid, world_taskid
13 print "Submitted tasks:", hello_taskid, world_taskid
14 print tc.get_task_result(hello_taskid,block=True).ns.word, tc.get_task_result(world_taskid,block=True).ns.word
14 print tc.get_task_result(hello_taskid,block=True).ns.word, tc.get_task_result(world_taskid,block=True).ns.word
@@ -31,7 +31,7 b' sigma_vals = N.linspace(0.0, 0.2,5)'
31 taskids = []
31 taskids = []
32 for K in K_vals:
32 for K in K_vals:
33 for sigma in sigma_vals:
33 for sigma in sigma_vals:
34 t = client.Task(task_string,
34 t = client.StringTask(task_string,
35 push=dict(sigma=sigma,K=K),
35 push=dict(sigma=sigma,K=K),
36 pull=('vp','ap','vc','ac','sigma','K'))
36 pull=('vp','ap','vc','ac','sigma','K'))
37 taskids.append(tc.run(t))
37 taskids.append(tc.run(t))
@@ -11,8 +11,8 b' b = 10*d'
11 c = a*b*d
11 c = a*b*d
12 """
12 """
13
13
14 t1 = client.Task(cmd1, clear_before=False, clear_after=True, pull=['a','b','c'])
14 t1 = client.StringTask(cmd1, clear_before=False, clear_after=True, pull=['a','b','c'])
15 tid1 = tc.run(t1)
15 tid1 = tc.run(t1)
16 tr1 = tc.get_task_result(tid1,block=True)
16 tr1 = tc.get_task_result(tid1,block=True)
17 tr1.raiseException()
17 tr1.raise_exception()
18 print "a, b: ", tr1.ns.a, tr1.ns.b No newline at end of file
18 print "a, b: ", tr1.ns.a, tr1.ns.b
@@ -10,7 +10,7 b' mec = client.MultiEngineClient()'
10 mec.execute('import time')
10 mec.execute('import time')
11
11
12 for i in range(24):
12 for i in range(24):
13 tc.irun('time.sleep(1)')
13 tc.run(client.StringTask('time.sleep(1)'))
14
14
15 for i in range(6):
15 for i in range(6):
16 time.sleep(1.0)
16 time.sleep(1.0)
@@ -18,7 +18,7 b' for i in range(6):'
18 print tc.queue_status()
18 print tc.queue_status()
19
19
20 for i in range(24):
20 for i in range(24):
21 tc.irun('time.sleep(1)')
21 tc.run(client.StringTask('time.sleep(1)'))
22
22
23 for i in range(6):
23 for i in range(6):
24 time.sleep(1.0)
24 time.sleep(1.0)
@@ -26,7 +26,7 b' for i in range(6):'
26 print tc.queue_status(True)
26 print tc.queue_status(True)
27
27
28 for i in range(12):
28 for i in range(12):
29 tc.irun('time.sleep(2)')
29 tc.run(client.StringTask('time.sleep(2)'))
30
30
31 print "Queue status (vebose=True)"
31 print "Queue status (vebose=True)"
32 print tc.queue_status(True)
32 print tc.queue_status(True)
@@ -55,7 +55,7 b' def main():'
55
55
56 # the jobs should take a random time within a range
56 # the jobs should take a random time within a range
57 times = [random.random()*(opts.tmax-opts.tmin)+opts.tmin for i in range(opts.n)]
57 times = [random.random()*(opts.tmax-opts.tmin)+opts.tmin for i in range(opts.n)]
58 tasks = [client.Task("time.sleep(%f)"%t) for t in times]
58 tasks = [client.StringTask("time.sleep(%f)"%t) for t in times]
59 stime = sum(times)
59 stime = sum(times)
60
60
61 print "executing %i tasks, totalling %.1f secs on %i engines"%(opts.n, stime, nengines)
61 print "executing %i tasks, totalling %.1f secs on %i engines"%(opts.n, stime, nengines)
@@ -12,6 +12,22 b' Release 0.9'
12 New features
12 New features
13 ------------
13 ------------
14
14
15 * The notion of a task has been completely reworked. An `ITask` interface has
16 been created. This interface defines the methods that tasks need to implement.
17 These methods are now responsible for things like submitting tasks and processing
18 results. There are two basic task types: :class:`IPython.kernel.task.StringTask`
19 (this is the old `Task` object, but renamed) and the new
20 :class:`IPython.kernel.task.MapTask`, which is based on a function.
21 * A new interface, :class:`IPython.kernel.mapper.IMapper` has been defined to
22 standardize the idea of a `map` method. This interface has a single
23 `map` method that has the same syntax as the built-in `map`. We have also defined
24 a `mapper` factory interface that creates objects that implement
25 :class:`IPython.kernel.mapper.IMapper` for different controllers. Both
26 the multiengine and task controller now have mapping capabilties.
27 * The parallel function capabilities have been reworks. The major changes are that
28 i) there is now an `@parallel` magic that creates parallel functions, ii)
29 the syntax for mulitple variable follows that of `map`, iii) both the
30 multiengine and task controller now have a parallel function implementation.
15 * All of the parallel computing capabilities from `ipython1-dev` have been merged into
31 * All of the parallel computing capabilities from `ipython1-dev` have been merged into
16 IPython proper. This resulted in the following new subpackages:
32 IPython proper. This resulted in the following new subpackages:
17 :mod:`IPython.kernel`, :mod:`IPython.kernel.core`, :mod:`IPython.config`,
33 :mod:`IPython.kernel`, :mod:`IPython.kernel.core`, :mod:`IPython.config`,
@@ -38,11 +54,11 b' New features'
38 when ipcluster is able to start things on other hosts, we will put security
54 when ipcluster is able to start things on other hosts, we will put security
39 back.
55 back.
40
56
41
42
43 Bug fixes
57 Bug fixes
44 ---------
58 ---------
45
59
60 * The colors escapes in the multiengine client are now turned off on win32 as they
61 don't print correctly.
46 * The :mod:`IPython.kernel.scripts.ipengine` script was exec'ing mpi_import_statement
62 * The :mod:`IPython.kernel.scripts.ipengine` script was exec'ing mpi_import_statement
47 incorrectly, which was leading the engine to crash when mpi was enabled.
63 incorrectly, which was leading the engine to crash when mpi was enabled.
48 * A few subpackages has missing `__init__.py` files.
64 * A few subpackages has missing `__init__.py` files.
@@ -52,6 +68,12 b' Bug fixes'
52 Backwards incompatible changes
68 Backwards incompatible changes
53 ------------------------------
69 ------------------------------
54
70
71 * :class:`IPython.kernel.client.Task` has been renamed
72 :class:`IPython.kernel.client.StringTask` to make way for new task types.
73 * The keyword argument `style` has been renamed `dist` in `scatter`, `gather`
74 and `map`.
75 * Renamed the values that the rename `dist` keyword argument can have from
76 `'basic'` to `'b'`.
55 * IPython has a larger set of dependencies if you want all of its capabilities.
77 * IPython has a larger set of dependencies if you want all of its capabilities.
56 See the `setup.py` script for details.
78 See the `setup.py` script for details.
57 * The constructors for :class:`IPython.kernel.client.MultiEngineClient` and
79 * The constructors for :class:`IPython.kernel.client.MultiEngineClient` and
@@ -1,3 +1,5 b''
1 .. _install_index:
2
1 ==================
3 ==================
2 Installation
4 Installation
3 ==================
5 ==================
@@ -4,18 +4,6 b''
4 Introduction
4 Introduction
5 ============
5 ============
6
6
7 This is the official documentation for IPython 0.x series (i.e. what
8 we are used to refer to just as "IPython"). The original text of the
9 manual (most of which is still in place) has been authored by Fernando
10 Perez, but as recommended usage patterns and new features have
11 emerged, this manual has been updated to reflect that fact. Most of
12 the additions have been authored by Ville M. Vainio.
13
14 The manual has been generated from reStructuredText source markup with
15 Sphinx, which should make it much easier to keep it up-to-date in the
16 future. Some reST artifacts and bugs may still be apparent in the
17 documentation, but this should improve as the toolchain matures.
18
19 Overview
7 Overview
20 ========
8 ========
21
9
@@ -25,8 +13,19 b' creating test files as is typical in most programming languages.'
25 However, the interpreter supplied with the standard Python distribution
13 However, the interpreter supplied with the standard Python distribution
26 is somewhat limited for extended interactive use.
14 is somewhat limited for extended interactive use.
27
15
28 IPython is a free software project (released under the BSD license)
16 The goal of IPython is to create a comprehensive environment for
29 which tries to:
17 interactive and exploratory computing. To support, this goal, IPython
18 has two main components:
19
20 * An enhanced interactive Python shell.
21 * An architecture for interactive parallel computing.
22
23 All of IPython is open source (released under the revised BSD license).
24
25 Enhanced interactive Python shell
26 =================================
27
28 IPython's interactive shell (`ipython`), has the following goals:
30
29
31 1. Provide an interactive shell superior to Python's default. IPython
30 1. Provide an interactive shell superior to Python's default. IPython
32 has many features for object introspection, system shell access,
31 has many features for object introspection, system shell access,
@@ -50,140 +49,126 b' which tries to:'
50 WX applications via special threading flags. The normal Python
49 WX applications via special threading flags. The normal Python
51 shell can only do this for Tkinter applications.
50 shell can only do this for Tkinter applications.
52
51
53
52 Main features of the interactive shell
54 Main features
53 --------------------------------------
55 -------------
54
56
55 * Dynamic object introspection. One can access docstrings, function
57 * Dynamic object introspection. One can access docstrings, function
56 definition prototypes, source code, source files and other details
58 definition prototypes, source code, source files and other details
57 of any object accessible to the interpreter with a single
59 of any object accessible to the interpreter with a single
58 keystroke (:samp:`?`, and using :samp:`??` provides additional detail).
60 keystroke ('?', and using '??' provides additional detail).
59 * Searching through modules and namespaces with :samp:`*` wildcards, both
61 * Searching through modules and namespaces with '*' wildcards, both
60 when using the :samp:`?` system and via the :samp:`%psearch` command.
62 when using the '?' system and via the %psearch command.
61 * Completion in the local namespace, by typing :kbd:`TAB` at the prompt.
63 * Completion in the local namespace, by typing TAB at the prompt.
62 This works for keywords, modules, methods, variables and files in the
64 This works for keywords, modules, methods, variables and files in the
63 current directory. This is supported via the readline library, and
65 current directory. This is supported via the readline library, and
64 full access to configuring readline's behavior is provided.
66 full access to configuring readline's behavior is provided.
65 Custom completers can be implemented easily for different purposes
67 Custom completers can be implemented easily for different purposes
66 (system commands, magic arguments etc.)
68 (system commands, magic arguments etc.)
67 * Numbered input/output prompts with command history (persistent
69 * Numbered input/output prompts with command history (persistent
68 across sessions and tied to each profile), full searching in this
70 across sessions and tied to each profile), full searching in this
69 history and caching of all input and output.
71 history and caching of all input and output.
70 * User-extensible 'magic' commands. A set of commands prefixed with
72 * User-extensible 'magic' commands. A set of commands prefixed with
71 :samp:`%` is available for controlling IPython itself and provides
73 % is available for controlling IPython itself and provides
72 directory control, namespace information and many aliases to
74 directory control, namespace information and many aliases to
73 common system shell commands.
75 common system shell commands.
74 * Alias facility for defining your own system aliases.
76 * Alias facility for defining your own system aliases.
75 * Complete system shell access. Lines starting with :samp:`!` are passed
77 * Complete system shell access. Lines starting with ! are passed
76 directly to the system shell, and using :samp:`!!` or :samp:`var = !cmd`
78 directly to the system shell, and using !! or var = !cmd
77 captures shell output into python variables for further use.
79 captures shell output into python variables for further use.
78 * Background execution of Python commands in a separate thread.
80 * Background execution of Python commands in a separate thread.
79 IPython has an internal job manager called jobs, and a
81 IPython has an internal job manager called jobs, and a
80 conveninence backgrounding magic function called :samp:`%bg`.
82 conveninence backgrounding magic function called %bg.
81 * The ability to expand python variables when calling the system
83 * The ability to expand python variables when calling the system
82 shell. In a shell command, any python variable prefixed with :samp:`$` is
84 shell. In a shell command, any python variable prefixed with $ is
83 expanded. A double :samp:`$$` allows passing a literal :samp:`$` to the shell (for
85 expanded. A double $$ allows passing a literal $ to the shell (for
84 access to shell and environment variables like :envvar:`PATH`).
86 access to shell and environment variables like $PATH).
85 * Filesystem navigation, via a magic :samp:`%cd` command, along with a
87 * Filesystem navigation, via a magic %cd command, along with a
86 persistent bookmark system (using :samp:`%bookmark`) for fast access to
88 persistent bookmark system (using %bookmark) for fast access to
87 frequently visited directories.
89 frequently visited directories.
88 * A lightweight persistence framework via the :samp:`%store` command, which
90 * A lightweight persistence framework via the %store command, which
89 allows you to save arbitrary Python variables. These get restored
91 allows you to save arbitrary Python variables. These get restored
90 automatically when your session restarts.
92 automatically when your session restarts.
91 * Automatic indentation (optional) of code as you type (through the
93 * Automatic indentation (optional) of code as you type (through the
92 readline library).
94 readline library).
93 * Macro system for quickly re-executing multiple lines of previous
95 * Macro system for quickly re-executing multiple lines of previous
94 input with a single name. Macros can be stored persistently via
96 input with a single name. Macros can be stored persistently via
95 :samp:`%store` and edited via :samp:`%edit`.
97 %store and edited via %edit.
96 * Session logging (you can then later use these logs as code in your
98 * Session logging (you can then later use these logs as code in your
97 programs). Logs can optionally timestamp all input, and also store
99 programs). Logs can optionally timestamp all input, and also store
98 session output (marked as comments, so the log remains valid
100 session output (marked as comments, so the log remains valid
99 Python source code).
101 Python source code).
100 * Session restoring: logs can be replayed to restore a previous
102 * Session restoring: logs can be replayed to restore a previous
101 session to the state where you left it.
103 session to the state where you left it.
102 * Verbose and colored exception traceback printouts. Easier to parse
104 * Verbose and colored exception traceback printouts. Easier to parse
103 visually, and in verbose mode they produce a lot of useful
105 visually, and in verbose mode they produce a lot of useful
104 debugging information (basically a terminal version of the cgitb
106 debugging information (basically a terminal version of the cgitb
105 module).
107 module).
106 * Auto-parentheses: callable objects can be executed without
108 * Auto-parentheses: callable objects can be executed without
107 parentheses: :samp:`sin 3` is automatically converted to :samp:`sin(3)`.
109 parentheses: 'sin 3' is automatically converted to 'sin(3)'.
108 * Auto-quoting: using :samp:`,`, or :samp:`;` as the first character forces
110 * Auto-quoting: using ',' or ';' as the first character forces
109 auto-quoting of the rest of the line: :samp:`,my_function a b` becomes
111 auto-quoting of the rest of the line: ',my_function a b' becomes
110 automatically :samp:`my_function("a","b")`, while :samp:`;my_function a b`
112 automatically 'my_function("a","b")', while ';my_function a b'
111 becomes :samp:`my_function("a b")`.
113 becomes 'my_function("a b")'.
112 * Extensible input syntax. You can define filters that pre-process
114 * Extensible input syntax. You can define filters that pre-process
113 user input to simplify input in special situations. This allows
115 user input to simplify input in special situations. This allows
114 for example pasting multi-line code fragments which start with
116 for example pasting multi-line code fragments which start with
115 :samp:`>>>` or :samp:`...` such as those from other python sessions or the
117 '>>>' or '...' such as those from other python sessions or the
116 standard Python documentation.
118 standard Python documentation.
117 * Flexible configuration system. It uses a configuration file which
119 * Flexible configuration system. It uses a configuration file which
118 allows permanent setting of all command-line options, module
120 allows permanent setting of all command-line options, module
119 loading, code and file execution. The system allows recursive file
121 loading, code and file execution. The system allows recursive file
120 inclusion, so you can have a base file with defaults and layers
122 inclusion, so you can have a base file with defaults and layers
121 which load other customizations for particular projects.
123 which load other customizations for particular projects.
122 * Embeddable. You can call IPython as a python shell inside your own
124 * Embeddable. You can call IPython as a python shell inside your own
123 python programs. This can be used both for debugging code or for
125 python programs. This can be used both for debugging code or for
124 providing interactive abilities to your programs with knowledge
126 providing interactive abilities to your programs with knowledge
125 about the local namespaces (very useful in debugging and data
127 about the local namespaces (very useful in debugging and data
126 analysis situations).
128 analysis situations).
127 * Easy debugger access. You can set IPython to call up an enhanced
129 * Easy debugger access. You can set IPython to call up an enhanced
128 version of the Python debugger (pdb) every time there is an
130 version of the Python debugger (pdb) every time there is an
129 uncaught exception. This drops you inside the code which triggered
131 uncaught exception. This drops you inside the code which triggered
130 the exception with all the data live and it is possible to
132 the exception with all the data live and it is possible to
131 navigate the stack to rapidly isolate the source of a bug. The
133 navigate the stack to rapidly isolate the source of a bug. The
132 :samp:`%run` magic command (with the :samp:`-d` option) can run any script under
134 %run magic command -with the -d option- can run any script under
133 pdb's control, automatically setting initial breakpoints for you.
135 pdb's control, automatically setting initial breakpoints for you.
134 This version of pdb has IPython-specific improvements, including
136 This version of pdb has IPython-specific improvements, including
135 tab-completion and traceback coloring support. For even easier
137 tab-completion and traceback coloring support. For even easier
136 debugger access, try :samp:`%debug` after seeing an exception. winpdb is
138 debugger access, try %debug after seeing an exception. winpdb is
137 also supported, see ipy_winpdb extension.
139 also supported, see ipy_winpdb extension.
138 * Profiler support. You can run single statements (similar to
140 * Profiler support. You can run single statements (similar to
139 :samp:`profile.run()`) or complete programs under the profiler's control.
141 profile.run()) or complete programs under the profiler's control.
140 While this is possible with standard cProfile or profile modules,
142 While this is possible with standard cProfile or profile modules,
141 IPython wraps this functionality with magic commands (see :samp:`%prun`
143 IPython wraps this functionality with magic commands (see '%prun'
142 and :samp:`%run -p`) convenient for rapid interactive work.
144 and '%run -p') convenient for rapid interactive work.
143 * Doctest support. The special :samp:`%doctest_mode` command toggles a mode
145 * Doctest support. The special %doctest_mode command toggles a mode
144 that allows you to paste existing doctests (with leading :samp:`>>>`
146 that allows you to paste existing doctests (with leading '>>>'
145 prompts and whitespace) and uses doctest-compatible prompts and
147 prompts and whitespace) and uses doctest-compatible prompts and
146 output, so you can use IPython sessions as doctest code.
148 output, so you can use IPython sessions as doctest code.
147
149
148 Interactive parallel computing
149 ==============================
150
151 Increasingly, parallel computer hardware, such as multicore CPUs, clusters and supercomputers, is becoming ubiquitous. Over the last 3 years, we have developed an
152 architecture within IPython that allows such hardware to be used quickly and easily
153 from Python. Moreover, this architecture is designed to support interactive and
154 collaborative parallel computing.
155
156 For more information, see our :ref:`overview <parallel_index>` of using IPython for
157 parallel computing.
150
158
151 Portability and Python requirements
159 Portability and Python requirements
152 -----------------------------------
160 -----------------------------------
153
161
154 Python requirements: IPython requires with Python version 2.3 or newer.
162 As of the 0.9 release, IPython requires Python 2.4 or greater. We have
155 If you are still using Python 2.2 and can not upgrade, the last version
163 not begun to test IPython on Python 2.6 or 3.0, but we expect it will
156 of IPython which worked with Python 2.2 was 0.6.15, so you will have to
164 work with some minor changes.
157 use that.
165
158
166 IPython is known to work on the following operating systems:
159 IPython is developed under Linux, but it should work in any reasonable
167
160 Unix-type system (tested OK under Solaris and the BSD family, for which
168 * Linux
161 a port exists thanks to Dryice Liu).
169 * AIX
162
170 * Most other Unix-like OSs (Solaris, BSD, etc.)
163 Mac OS X: it works, apparently without any problems (thanks to Jim Boyle
171 * Mac OS X
164 at Lawrence Livermore for the information). Thanks to Andrea Riciputi,
172 * Windows (CygWin, XP, Vista, etc.)
165 Fink support is available.
173
166
174 See :ref:`here <install_index>` for instructions on how to install IPython. No newline at end of file
167 CygWin: it works mostly OK, though some users have reported problems
168 with prompt coloring. No satisfactory solution to this has been found so
169 far, you may want to disable colors permanently in the ipythonrc
170 configuration file if you experience problems. If you have proper color
171 support under cygwin, please post to the IPython mailing list so this
172 issue can be resolved for all users.
173
174 Windows: it works well under Windows Vista/XP/2k, and I suspect NT should
175 behave similarly. Section "Installation under windows" describes
176 installation details for Windows, including some additional tools needed
177 on this platform.
178
179 Windows 9x support is present, and has been reported to work fine (at
180 least on WinME).
181
182 Location
183 --------
184
185 IPython is generously hosted at http://ipython.scipy.org by the
186 Enthought, Inc and the SciPy project. This site offers downloads,
187 subversion access, mailing lists and a bug tracking system. I am very
188 grateful to Enthought (http://www.enthought.com) and all of the SciPy
189 team for their contribution. No newline at end of file
@@ -1,3 +1,5 b''
1 .. _parallel_index:
2
1 ====================================
3 ====================================
2 Using IPython for Parallel computing
4 Using IPython for Parallel computing
3 ====================================
5 ====================================
@@ -24,4 +24,5 b' are trapped first by Python itself.'
24 """
24 """
25
25
26 import IPython.Shell
26 import IPython.Shell
27
27 IPython.Shell.start().mainloop()
28 IPython.Shell.start().mainloop()
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