Show More
@@ -1,282 +1,285 b'' | |||
|
1 | 1 | """Remote Functions and decorators for Views. |
|
2 | 2 | |
|
3 | 3 | Authors: |
|
4 | 4 | |
|
5 | 5 | * Brian Granger |
|
6 | 6 | * Min RK |
|
7 | 7 | """ |
|
8 | 8 | #----------------------------------------------------------------------------- |
|
9 | 9 | # Copyright (C) 2010-2011 The IPython Development Team |
|
10 | 10 | # |
|
11 | 11 | # Distributed under the terms of the BSD License. The full license is in |
|
12 | 12 | # the file COPYING, distributed as part of this software. |
|
13 | 13 | #----------------------------------------------------------------------------- |
|
14 | 14 | |
|
15 | 15 | #----------------------------------------------------------------------------- |
|
16 | 16 | # Imports |
|
17 | 17 | #----------------------------------------------------------------------------- |
|
18 | 18 | |
|
19 | 19 | from __future__ import division |
|
20 | 20 | |
|
21 | 21 | import sys |
|
22 | 22 | import warnings |
|
23 | 23 | |
|
24 | 24 | from IPython.external.decorator import decorator |
|
25 | 25 | from IPython.testing.skipdoctest import skip_doctest |
|
26 | 26 | |
|
27 | 27 | from . import map as Map |
|
28 | 28 | from .asyncresult import AsyncMapResult |
|
29 | 29 | |
|
30 | 30 | #----------------------------------------------------------------------------- |
|
31 | 31 | # Functions and Decorators |
|
32 | 32 | #----------------------------------------------------------------------------- |
|
33 | 33 | |
|
34 | 34 | @skip_doctest |
|
35 | 35 | def remote(view, block=None, **flags): |
|
36 | 36 | """Turn a function into a remote function. |
|
37 | 37 | |
|
38 | 38 | This method can be used for map: |
|
39 | 39 | |
|
40 | 40 | In [1]: @remote(view,block=True) |
|
41 | 41 | ...: def func(a): |
|
42 | 42 | ...: pass |
|
43 | 43 | """ |
|
44 | 44 | |
|
45 | 45 | def remote_function(f): |
|
46 | 46 | return RemoteFunction(view, f, block=block, **flags) |
|
47 | 47 | return remote_function |
|
48 | 48 | |
|
49 | 49 | @skip_doctest |
|
50 | 50 | def parallel(view, dist='b', block=None, ordered=True, **flags): |
|
51 | 51 | """Turn a function into a parallel remote function. |
|
52 | 52 | |
|
53 | 53 | This method can be used for map: |
|
54 | 54 | |
|
55 | 55 | In [1]: @parallel(view, block=True) |
|
56 | 56 | ...: def func(a): |
|
57 | 57 | ...: pass |
|
58 | 58 | """ |
|
59 | 59 | |
|
60 | 60 | def parallel_function(f): |
|
61 | 61 | return ParallelFunction(view, f, dist=dist, block=block, ordered=ordered, **flags) |
|
62 | 62 | return parallel_function |
|
63 | 63 | |
|
64 | 64 | def getname(f): |
|
65 | 65 | """Get the name of an object. |
|
66 | 66 | |
|
67 | 67 | For use in case of callables that are not functions, and |
|
68 | 68 | thus may not have __name__ defined. |
|
69 | 69 | |
|
70 | 70 | Order: f.__name__ > f.name > str(f) |
|
71 | 71 | """ |
|
72 | 72 | try: |
|
73 | 73 | return f.__name__ |
|
74 | 74 | except: |
|
75 | 75 | pass |
|
76 | 76 | try: |
|
77 | 77 | return f.name |
|
78 | 78 | except: |
|
79 | 79 | pass |
|
80 | 80 | |
|
81 | 81 | return str(f) |
|
82 | 82 | |
|
83 | 83 | @decorator |
|
84 | 84 | def sync_view_results(f, self, *args, **kwargs): |
|
85 | 85 | """sync relevant results from self.client to our results attribute. |
|
86 | 86 | |
|
87 | 87 | This is a clone of view.sync_results, but for remote functions |
|
88 | 88 | """ |
|
89 | 89 | view = self.view |
|
90 | 90 | if view._in_sync_results: |
|
91 | 91 | return f(self, *args, **kwargs) |
|
92 | 92 | view._in_sync_results = True |
|
93 | 93 | try: |
|
94 | 94 | ret = f(self, *args, **kwargs) |
|
95 | 95 | finally: |
|
96 | 96 | view._in_sync_results = False |
|
97 | 97 | view._sync_results() |
|
98 | 98 | return ret |
|
99 | 99 | |
|
100 | 100 | #-------------------------------------------------------------------------- |
|
101 | 101 | # Classes |
|
102 | 102 | #-------------------------------------------------------------------------- |
|
103 | 103 | |
|
104 | 104 | class RemoteFunction(object): |
|
105 | 105 | """Turn an existing function into a remote function. |
|
106 | 106 | |
|
107 | 107 | Parameters |
|
108 | 108 | ---------- |
|
109 | 109 | |
|
110 | 110 | view : View instance |
|
111 | 111 | The view to be used for execution |
|
112 | 112 | f : callable |
|
113 | 113 | The function to be wrapped into a remote function |
|
114 | 114 | block : bool [default: None] |
|
115 | 115 | Whether to wait for results or not. The default behavior is |
|
116 | 116 | to use the current `block` attribute of `view` |
|
117 | 117 | |
|
118 | 118 | **flags : remaining kwargs are passed to View.temp_flags |
|
119 | 119 | """ |
|
120 | 120 | |
|
121 | 121 | view = None # the remote connection |
|
122 | 122 | func = None # the wrapped function |
|
123 | 123 | block = None # whether to block |
|
124 | 124 | flags = None # dict of extra kwargs for temp_flags |
|
125 | 125 | |
|
126 | 126 | def __init__(self, view, f, block=None, **flags): |
|
127 | 127 | self.view = view |
|
128 | 128 | self.func = f |
|
129 | 129 | self.block=block |
|
130 | 130 | self.flags=flags |
|
131 | 131 | |
|
132 | 132 | def __call__(self, *args, **kwargs): |
|
133 | 133 | block = self.view.block if self.block is None else self.block |
|
134 | 134 | with self.view.temp_flags(block=block, **self.flags): |
|
135 | 135 | return self.view.apply(self.func, *args, **kwargs) |
|
136 | 136 | |
|
137 | 137 | |
|
138 | 138 | class ParallelFunction(RemoteFunction): |
|
139 | 139 | """Class for mapping a function to sequences. |
|
140 | 140 | |
|
141 | 141 | This will distribute the sequences according the a mapper, and call |
|
142 | 142 | the function on each sub-sequence. If called via map, then the function |
|
143 | 143 | will be called once on each element, rather that each sub-sequence. |
|
144 | 144 | |
|
145 | 145 | Parameters |
|
146 | 146 | ---------- |
|
147 | 147 | |
|
148 | 148 | view : View instance |
|
149 | 149 | The view to be used for execution |
|
150 | 150 | f : callable |
|
151 | 151 | The function to be wrapped into a remote function |
|
152 | 152 | dist : str [default: 'b'] |
|
153 | 153 | The key for which mapObject to use to distribute sequences |
|
154 | 154 | options are: |
|
155 | * 'b' : use contiguous chunks in order | |
|
156 | * 'r' : use round-robin striping | |
|
155 | ||
|
156 | * 'b' : use contiguous chunks in order | |
|
157 | * 'r' : use round-robin striping | |
|
158 | ||
|
157 | 159 | block : bool [default: None] |
|
158 | 160 | Whether to wait for results or not. The default behavior is |
|
159 | 161 | to use the current `block` attribute of `view` |
|
160 | 162 | chunksize : int or None |
|
161 | 163 | The size of chunk to use when breaking up sequences in a load-balanced manner |
|
162 | 164 | ordered : bool [default: True] |
|
163 | 165 | Whether the result should be kept in order. If False, |
|
164 | 166 | results become available as they arrive, regardless of submission order. |
|
165 | **flags : remaining kwargs are passed to View.temp_flags | |
|
167 | **flags | |
|
168 | remaining kwargs are passed to View.temp_flags | |
|
166 | 169 | """ |
|
167 | 170 | |
|
168 | 171 | chunksize = None |
|
169 | 172 | ordered = None |
|
170 | 173 | mapObject = None |
|
171 | 174 | _mapping = False |
|
172 | 175 | |
|
173 | 176 | def __init__(self, view, f, dist='b', block=None, chunksize=None, ordered=True, **flags): |
|
174 | 177 | super(ParallelFunction, self).__init__(view, f, block=block, **flags) |
|
175 | 178 | self.chunksize = chunksize |
|
176 | 179 | self.ordered = ordered |
|
177 | 180 | |
|
178 | 181 | mapClass = Map.dists[dist] |
|
179 | 182 | self.mapObject = mapClass() |
|
180 | 183 | |
|
181 | 184 | @sync_view_results |
|
182 | 185 | def __call__(self, *sequences): |
|
183 | 186 | client = self.view.client |
|
184 | 187 | |
|
185 | 188 | lens = [] |
|
186 | 189 | maxlen = minlen = -1 |
|
187 | 190 | for i, seq in enumerate(sequences): |
|
188 | 191 | try: |
|
189 | 192 | n = len(seq) |
|
190 | 193 | except Exception: |
|
191 | 194 | seq = list(seq) |
|
192 | 195 | if isinstance(sequences, tuple): |
|
193 | 196 | # can't alter a tuple |
|
194 | 197 | sequences = list(sequences) |
|
195 | 198 | sequences[i] = seq |
|
196 | 199 | n = len(seq) |
|
197 | 200 | if n > maxlen: |
|
198 | 201 | maxlen = n |
|
199 | 202 | if minlen == -1 or n < minlen: |
|
200 | 203 | minlen = n |
|
201 | 204 | lens.append(n) |
|
202 | 205 | |
|
203 | 206 | # check that the length of sequences match |
|
204 | 207 | if not self._mapping and minlen != maxlen: |
|
205 | 208 | msg = 'all sequences must have equal length, but have %s' % lens |
|
206 | 209 | raise ValueError(msg) |
|
207 | 210 | |
|
208 | 211 | balanced = 'Balanced' in self.view.__class__.__name__ |
|
209 | 212 | if balanced: |
|
210 | 213 | if self.chunksize: |
|
211 | 214 | nparts = maxlen // self.chunksize + int(maxlen % self.chunksize > 0) |
|
212 | 215 | else: |
|
213 | 216 | nparts = maxlen |
|
214 | 217 | targets = [None]*nparts |
|
215 | 218 | else: |
|
216 | 219 | if self.chunksize: |
|
217 | 220 | warnings.warn("`chunksize` is ignored unless load balancing", UserWarning) |
|
218 | 221 | # multiplexed: |
|
219 | 222 | targets = self.view.targets |
|
220 | 223 | # 'all' is lazily evaluated at execution time, which is now: |
|
221 | 224 | if targets == 'all': |
|
222 | 225 | targets = client._build_targets(targets)[1] |
|
223 | 226 | elif isinstance(targets, int): |
|
224 | 227 | # single-engine view, targets must be iterable |
|
225 | 228 | targets = [targets] |
|
226 | 229 | nparts = len(targets) |
|
227 | 230 | |
|
228 | 231 | msg_ids = [] |
|
229 | 232 | for index, t in enumerate(targets): |
|
230 | 233 | args = [] |
|
231 | 234 | for seq in sequences: |
|
232 | 235 | part = self.mapObject.getPartition(seq, index, nparts, maxlen) |
|
233 | 236 | args.append(part) |
|
234 | 237 | |
|
235 | 238 | if sum([len(arg) for arg in args]) == 0: |
|
236 | 239 | continue |
|
237 | 240 | |
|
238 | 241 | if self._mapping: |
|
239 | 242 | if sys.version_info[0] >= 3: |
|
240 | 243 | f = lambda f, *sequences: list(map(f, *sequences)) |
|
241 | 244 | else: |
|
242 | 245 | f = map |
|
243 | 246 | args = [self.func] + args |
|
244 | 247 | else: |
|
245 | 248 | f=self.func |
|
246 | 249 | |
|
247 | 250 | view = self.view if balanced else client[t] |
|
248 | 251 | with view.temp_flags(block=False, **self.flags): |
|
249 | 252 | ar = view.apply(f, *args) |
|
250 | 253 | |
|
251 | 254 | msg_ids.extend(ar.msg_ids) |
|
252 | 255 | |
|
253 | 256 | r = AsyncMapResult(self.view.client, msg_ids, self.mapObject, |
|
254 | 257 | fname=getname(self.func), |
|
255 | 258 | ordered=self.ordered |
|
256 | 259 | ) |
|
257 | 260 | |
|
258 | 261 | if self.block: |
|
259 | 262 | try: |
|
260 | 263 | return r.get() |
|
261 | 264 | except KeyboardInterrupt: |
|
262 | 265 | return r |
|
263 | 266 | else: |
|
264 | 267 | return r |
|
265 | 268 | |
|
266 | 269 | def map(self, *sequences): |
|
267 | 270 | """call a function on each element of one or more sequence(s) remotely. |
|
268 | 271 | This should behave very much like the builtin map, but return an AsyncMapResult |
|
269 | 272 | if self.block is False. |
|
270 | 273 | |
|
271 | 274 | That means it can take generators (will be cast to lists locally), |
|
272 | 275 | and mismatched sequence lengths will be padded with None. |
|
273 | 276 | """ |
|
274 | 277 | # set _mapping as a flag for use inside self.__call__ |
|
275 | 278 | self._mapping = True |
|
276 | 279 | try: |
|
277 | 280 | ret = self(*sequences) |
|
278 | 281 | finally: |
|
279 | 282 | self._mapping = False |
|
280 | 283 | return ret |
|
281 | 284 | |
|
282 | 285 | __all__ = ['remote', 'parallel', 'RemoteFunction', 'ParallelFunction'] |
@@ -1,1119 +1,1114 b'' | |||
|
1 | 1 | """Views of remote engines. |
|
2 | 2 | |
|
3 | 3 | Authors: |
|
4 | 4 | |
|
5 | 5 | * Min RK |
|
6 | 6 | """ |
|
7 | 7 | from __future__ import print_function |
|
8 | 8 | #----------------------------------------------------------------------------- |
|
9 | 9 | # Copyright (C) 2010-2011 The IPython Development Team |
|
10 | 10 | # |
|
11 | 11 | # Distributed under the terms of the BSD License. The full license is in |
|
12 | 12 | # the file COPYING, distributed as part of this software. |
|
13 | 13 | #----------------------------------------------------------------------------- |
|
14 | 14 | |
|
15 | 15 | #----------------------------------------------------------------------------- |
|
16 | 16 | # Imports |
|
17 | 17 | #----------------------------------------------------------------------------- |
|
18 | 18 | |
|
19 | 19 | import imp |
|
20 | 20 | import sys |
|
21 | 21 | import warnings |
|
22 | 22 | from contextlib import contextmanager |
|
23 | 23 | from types import ModuleType |
|
24 | 24 | |
|
25 | 25 | import zmq |
|
26 | 26 | |
|
27 | 27 | from IPython.testing.skipdoctest import skip_doctest |
|
28 | 28 | from IPython.utils.traitlets import ( |
|
29 | 29 | HasTraits, Any, Bool, List, Dict, Set, Instance, CFloat, Integer |
|
30 | 30 | ) |
|
31 | 31 | from IPython.external.decorator import decorator |
|
32 | 32 | |
|
33 | 33 | from IPython.parallel import util |
|
34 | 34 | from IPython.parallel.controller.dependency import Dependency, dependent |
|
35 | 35 | from IPython.utils.py3compat import string_types, iteritems, PY3 |
|
36 | 36 | |
|
37 | 37 | from . import map as Map |
|
38 | 38 | from .asyncresult import AsyncResult, AsyncMapResult |
|
39 | 39 | from .remotefunction import ParallelFunction, parallel, remote, getname |
|
40 | 40 | |
|
41 | 41 | #----------------------------------------------------------------------------- |
|
42 | 42 | # Decorators |
|
43 | 43 | #----------------------------------------------------------------------------- |
|
44 | 44 | |
|
45 | 45 | @decorator |
|
46 | 46 | def save_ids(f, self, *args, **kwargs): |
|
47 | 47 | """Keep our history and outstanding attributes up to date after a method call.""" |
|
48 | 48 | n_previous = len(self.client.history) |
|
49 | 49 | try: |
|
50 | 50 | ret = f(self, *args, **kwargs) |
|
51 | 51 | finally: |
|
52 | 52 | nmsgs = len(self.client.history) - n_previous |
|
53 | 53 | msg_ids = self.client.history[-nmsgs:] |
|
54 | 54 | self.history.extend(msg_ids) |
|
55 | 55 | self.outstanding.update(msg_ids) |
|
56 | 56 | return ret |
|
57 | 57 | |
|
58 | 58 | @decorator |
|
59 | 59 | def sync_results(f, self, *args, **kwargs): |
|
60 | 60 | """sync relevant results from self.client to our results attribute.""" |
|
61 | 61 | if self._in_sync_results: |
|
62 | 62 | return f(self, *args, **kwargs) |
|
63 | 63 | self._in_sync_results = True |
|
64 | 64 | try: |
|
65 | 65 | ret = f(self, *args, **kwargs) |
|
66 | 66 | finally: |
|
67 | 67 | self._in_sync_results = False |
|
68 | 68 | self._sync_results() |
|
69 | 69 | return ret |
|
70 | 70 | |
|
71 | 71 | @decorator |
|
72 | 72 | def spin_after(f, self, *args, **kwargs): |
|
73 | 73 | """call spin after the method.""" |
|
74 | 74 | ret = f(self, *args, **kwargs) |
|
75 | 75 | self.spin() |
|
76 | 76 | return ret |
|
77 | 77 | |
|
78 | 78 | #----------------------------------------------------------------------------- |
|
79 | 79 | # Classes |
|
80 | 80 | #----------------------------------------------------------------------------- |
|
81 | 81 | |
|
82 | 82 | @skip_doctest |
|
83 | 83 | class View(HasTraits): |
|
84 | 84 | """Base View class for more convenint apply(f,*args,**kwargs) syntax via attributes. |
|
85 | 85 | |
|
86 | 86 | Don't use this class, use subclasses. |
|
87 | 87 | |
|
88 | 88 | Methods |
|
89 | 89 | ------- |
|
90 | 90 | |
|
91 | 91 | spin |
|
92 | 92 | flushes incoming results and registration state changes |
|
93 | 93 | control methods spin, and requesting `ids` also ensures up to date |
|
94 | 94 | |
|
95 | 95 | wait |
|
96 | 96 | wait on one or more msg_ids |
|
97 | 97 | |
|
98 | 98 | execution methods |
|
99 | 99 | apply |
|
100 | 100 | legacy: execute, run |
|
101 | 101 | |
|
102 | 102 | data movement |
|
103 | 103 | push, pull, scatter, gather |
|
104 | 104 | |
|
105 | 105 | query methods |
|
106 | 106 | get_result, queue_status, purge_results, result_status |
|
107 | 107 | |
|
108 | 108 | control methods |
|
109 | 109 | abort, shutdown |
|
110 | 110 | |
|
111 | 111 | """ |
|
112 | 112 | # flags |
|
113 | 113 | block=Bool(False) |
|
114 | 114 | track=Bool(True) |
|
115 | 115 | targets = Any() |
|
116 | 116 | |
|
117 | 117 | history=List() |
|
118 | 118 | outstanding = Set() |
|
119 | 119 | results = Dict() |
|
120 | 120 | client = Instance('IPython.parallel.Client') |
|
121 | 121 | |
|
122 | 122 | _socket = Instance('zmq.Socket') |
|
123 | 123 | _flag_names = List(['targets', 'block', 'track']) |
|
124 | 124 | _in_sync_results = Bool(False) |
|
125 | 125 | _targets = Any() |
|
126 | 126 | _idents = Any() |
|
127 | 127 | |
|
128 | 128 | def __init__(self, client=None, socket=None, **flags): |
|
129 | 129 | super(View, self).__init__(client=client, _socket=socket) |
|
130 | 130 | self.results = client.results |
|
131 | 131 | self.block = client.block |
|
132 | 132 | |
|
133 | 133 | self.set_flags(**flags) |
|
134 | 134 | |
|
135 | 135 | assert not self.__class__ is View, "Don't use base View objects, use subclasses" |
|
136 | 136 | |
|
137 | 137 | def __repr__(self): |
|
138 | 138 | strtargets = str(self.targets) |
|
139 | 139 | if len(strtargets) > 16: |
|
140 | 140 | strtargets = strtargets[:12]+'...]' |
|
141 | 141 | return "<%s %s>"%(self.__class__.__name__, strtargets) |
|
142 | 142 | |
|
143 | 143 | def __len__(self): |
|
144 | 144 | if isinstance(self.targets, list): |
|
145 | 145 | return len(self.targets) |
|
146 | 146 | elif isinstance(self.targets, int): |
|
147 | 147 | return 1 |
|
148 | 148 | else: |
|
149 | 149 | return len(self.client) |
|
150 | 150 | |
|
151 | 151 | def set_flags(self, **kwargs): |
|
152 | 152 | """set my attribute flags by keyword. |
|
153 | 153 | |
|
154 | 154 | Views determine behavior with a few attributes (`block`, `track`, etc.). |
|
155 | 155 | These attributes can be set all at once by name with this method. |
|
156 | 156 | |
|
157 | 157 | Parameters |
|
158 | 158 | ---------- |
|
159 | 159 | |
|
160 | 160 | block : bool |
|
161 | 161 | whether to wait for results |
|
162 | 162 | track : bool |
|
163 | 163 | whether to create a MessageTracker to allow the user to |
|
164 | 164 | safely edit after arrays and buffers during non-copying |
|
165 | 165 | sends. |
|
166 | 166 | """ |
|
167 | 167 | for name, value in iteritems(kwargs): |
|
168 | 168 | if name not in self._flag_names: |
|
169 | 169 | raise KeyError("Invalid name: %r"%name) |
|
170 | 170 | else: |
|
171 | 171 | setattr(self, name, value) |
|
172 | 172 | |
|
173 | 173 | @contextmanager |
|
174 | 174 | def temp_flags(self, **kwargs): |
|
175 | 175 | """temporarily set flags, for use in `with` statements. |
|
176 | 176 | |
|
177 | 177 | See set_flags for permanent setting of flags |
|
178 | 178 | |
|
179 | 179 | Examples |
|
180 | 180 | -------- |
|
181 | 181 | |
|
182 | 182 | >>> view.track=False |
|
183 | 183 | ... |
|
184 | 184 | >>> with view.temp_flags(track=True): |
|
185 | 185 | ... ar = view.apply(dostuff, my_big_array) |
|
186 | 186 | ... ar.tracker.wait() # wait for send to finish |
|
187 | 187 | >>> view.track |
|
188 | 188 | False |
|
189 | 189 | |
|
190 | 190 | """ |
|
191 | 191 | # preflight: save flags, and set temporaries |
|
192 | 192 | saved_flags = {} |
|
193 | 193 | for f in self._flag_names: |
|
194 | 194 | saved_flags[f] = getattr(self, f) |
|
195 | 195 | self.set_flags(**kwargs) |
|
196 | 196 | # yield to the with-statement block |
|
197 | 197 | try: |
|
198 | 198 | yield |
|
199 | 199 | finally: |
|
200 | 200 | # postflight: restore saved flags |
|
201 | 201 | self.set_flags(**saved_flags) |
|
202 | 202 | |
|
203 | 203 | |
|
204 | 204 | #---------------------------------------------------------------- |
|
205 | 205 | # apply |
|
206 | 206 | #---------------------------------------------------------------- |
|
207 | 207 | |
|
208 | 208 | def _sync_results(self): |
|
209 | 209 | """to be called by @sync_results decorator |
|
210 | 210 | |
|
211 | 211 | after submitting any tasks. |
|
212 | 212 | """ |
|
213 | 213 | delta = self.outstanding.difference(self.client.outstanding) |
|
214 | 214 | completed = self.outstanding.intersection(delta) |
|
215 | 215 | self.outstanding = self.outstanding.difference(completed) |
|
216 | 216 | |
|
217 | 217 | @sync_results |
|
218 | 218 | @save_ids |
|
219 | 219 | def _really_apply(self, f, args, kwargs, block=None, **options): |
|
220 | 220 | """wrapper for client.send_apply_request""" |
|
221 | 221 | raise NotImplementedError("Implement in subclasses") |
|
222 | 222 | |
|
223 | 223 | def apply(self, f, *args, **kwargs): |
|
224 | """calls f(*args, **kwargs) on remote engines, returning the result. | |
|
224 | """calls ``f(*args, **kwargs)`` on remote engines, returning the result. | |
|
225 | 225 | |
|
226 | 226 | This method sets all apply flags via this View's attributes. |
|
227 | 227 | |
|
228 | if self.block is False: | |
|
229 | returns AsyncResult | |
|
230 | else: | |
|
231 | returns actual result of f(*args, **kwargs) | |
|
228 | Returns :class:`~IPython.parallel.client.asyncresult.AsyncResult` | |
|
229 | instance if ``self.block`` is False, otherwise the return value of | |
|
230 | ``f(*args, **kwargs)``. | |
|
232 | 231 | """ |
|
233 | 232 | return self._really_apply(f, args, kwargs) |
|
234 | 233 | |
|
235 | 234 | def apply_async(self, f, *args, **kwargs): |
|
236 | """calls f(*args, **kwargs) on remote engines in a nonblocking manner. | |
|
235 | """calls ``f(*args, **kwargs)`` on remote engines in a nonblocking manner. | |
|
237 | 236 | |
|
238 | returns AsyncResult | |
|
237 | Returns :class:`~IPython.parallel.client.asyncresult.AsyncResult` instance. | |
|
239 | 238 | """ |
|
240 | 239 | return self._really_apply(f, args, kwargs, block=False) |
|
241 | 240 | |
|
242 | 241 | @spin_after |
|
243 | 242 | def apply_sync(self, f, *args, **kwargs): |
|
244 | """calls f(*args, **kwargs) on remote engines in a blocking manner, | |
|
243 | """calls ``f(*args, **kwargs)`` on remote engines in a blocking manner, | |
|
245 | 244 | returning the result. |
|
246 | ||
|
247 | returns: actual result of f(*args, **kwargs) | |
|
248 | 245 | """ |
|
249 | 246 | return self._really_apply(f, args, kwargs, block=True) |
|
250 | 247 | |
|
251 | 248 | #---------------------------------------------------------------- |
|
252 | 249 | # wrappers for client and control methods |
|
253 | 250 | #---------------------------------------------------------------- |
|
254 | 251 | @sync_results |
|
255 | 252 | def spin(self): |
|
256 | 253 | """spin the client, and sync""" |
|
257 | 254 | self.client.spin() |
|
258 | 255 | |
|
259 | 256 | @sync_results |
|
260 | 257 | def wait(self, jobs=None, timeout=-1): |
|
261 | 258 | """waits on one or more `jobs`, for up to `timeout` seconds. |
|
262 | 259 | |
|
263 | 260 | Parameters |
|
264 | 261 | ---------- |
|
265 | 262 | |
|
266 | 263 | jobs : int, str, or list of ints and/or strs, or one or more AsyncResult objects |
|
267 | 264 | ints are indices to self.history |
|
268 | 265 | strs are msg_ids |
|
269 | 266 | default: wait on all outstanding messages |
|
270 | 267 | timeout : float |
|
271 | 268 | a time in seconds, after which to give up. |
|
272 | 269 | default is -1, which means no timeout |
|
273 | 270 | |
|
274 | 271 | Returns |
|
275 | 272 | ------- |
|
276 | 273 | |
|
277 | 274 | True : when all msg_ids are done |
|
278 | 275 | False : timeout reached, some msg_ids still outstanding |
|
279 | 276 | """ |
|
280 | 277 | if jobs is None: |
|
281 | 278 | jobs = self.history |
|
282 | 279 | return self.client.wait(jobs, timeout) |
|
283 | 280 | |
|
284 | 281 | def abort(self, jobs=None, targets=None, block=None): |
|
285 | 282 | """Abort jobs on my engines. |
|
286 | 283 | |
|
287 | 284 | Parameters |
|
288 | 285 | ---------- |
|
289 | 286 | |
|
290 | 287 | jobs : None, str, list of strs, optional |
|
291 | 288 | if None: abort all jobs. |
|
292 | 289 | else: abort specific msg_id(s). |
|
293 | 290 | """ |
|
294 | 291 | block = block if block is not None else self.block |
|
295 | 292 | targets = targets if targets is not None else self.targets |
|
296 | 293 | jobs = jobs if jobs is not None else list(self.outstanding) |
|
297 | 294 | |
|
298 | 295 | return self.client.abort(jobs=jobs, targets=targets, block=block) |
|
299 | 296 | |
|
300 | 297 | def queue_status(self, targets=None, verbose=False): |
|
301 | 298 | """Fetch the Queue status of my engines""" |
|
302 | 299 | targets = targets if targets is not None else self.targets |
|
303 | 300 | return self.client.queue_status(targets=targets, verbose=verbose) |
|
304 | 301 | |
|
305 | 302 | def purge_results(self, jobs=[], targets=[]): |
|
306 | 303 | """Instruct the controller to forget specific results.""" |
|
307 | 304 | if targets is None or targets == 'all': |
|
308 | 305 | targets = self.targets |
|
309 | 306 | return self.client.purge_results(jobs=jobs, targets=targets) |
|
310 | 307 | |
|
311 | 308 | def shutdown(self, targets=None, restart=False, hub=False, block=None): |
|
312 | 309 | """Terminates one or more engine processes, optionally including the hub. |
|
313 | 310 | """ |
|
314 | 311 | block = self.block if block is None else block |
|
315 | 312 | if targets is None or targets == 'all': |
|
316 | 313 | targets = self.targets |
|
317 | 314 | return self.client.shutdown(targets=targets, restart=restart, hub=hub, block=block) |
|
318 | 315 | |
|
319 | 316 | @spin_after |
|
320 | 317 | def get_result(self, indices_or_msg_ids=None): |
|
321 | 318 | """return one or more results, specified by history index or msg_id. |
|
322 | 319 | |
|
323 | See client.get_result for details. | |
|
324 | ||
|
320 | See :meth:`IPython.parallel.client.client.Client.get_result` for details. | |
|
325 | 321 | """ |
|
326 | 322 | |
|
327 | 323 | if indices_or_msg_ids is None: |
|
328 | 324 | indices_or_msg_ids = -1 |
|
329 | 325 | if isinstance(indices_or_msg_ids, int): |
|
330 | 326 | indices_or_msg_ids = self.history[indices_or_msg_ids] |
|
331 | 327 | elif isinstance(indices_or_msg_ids, (list,tuple,set)): |
|
332 | 328 | indices_or_msg_ids = list(indices_or_msg_ids) |
|
333 | 329 | for i,index in enumerate(indices_or_msg_ids): |
|
334 | 330 | if isinstance(index, int): |
|
335 | 331 | indices_or_msg_ids[i] = self.history[index] |
|
336 | 332 | return self.client.get_result(indices_or_msg_ids) |
|
337 | 333 | |
|
338 | 334 | #------------------------------------------------------------------- |
|
339 | 335 | # Map |
|
340 | 336 | #------------------------------------------------------------------- |
|
341 | 337 | |
|
342 | 338 | @sync_results |
|
343 | 339 | def map(self, f, *sequences, **kwargs): |
|
344 | 340 | """override in subclasses""" |
|
345 | 341 | raise NotImplementedError |
|
346 | 342 | |
|
347 | 343 | def map_async(self, f, *sequences, **kwargs): |
|
348 | """Parallel version of builtin `map`, using this view's engines. | |
|
344 | """Parallel version of builtin :func:`python:map`, using this view's engines. | |
|
349 | 345 | |
|
350 | This is equivalent to map(...block=False) | |
|
346 | This is equivalent to ``map(...block=False)``. | |
|
351 | 347 | |
|
352 | 348 | See `self.map` for details. |
|
353 | 349 | """ |
|
354 | 350 | if 'block' in kwargs: |
|
355 | 351 | raise TypeError("map_async doesn't take a `block` keyword argument.") |
|
356 | 352 | kwargs['block'] = False |
|
357 | 353 | return self.map(f,*sequences,**kwargs) |
|
358 | 354 | |
|
359 | 355 | def map_sync(self, f, *sequences, **kwargs): |
|
360 | """Parallel version of builtin `map`, using this view's engines. | |
|
356 | """Parallel version of builtin :func:`python:map`, using this view's engines. | |
|
361 | 357 | |
|
362 | This is equivalent to map(...block=True) | |
|
358 | This is equivalent to ``map(...block=True)``. | |
|
363 | 359 | |
|
364 | 360 | See `self.map` for details. |
|
365 | 361 | """ |
|
366 | 362 | if 'block' in kwargs: |
|
367 | 363 | raise TypeError("map_sync doesn't take a `block` keyword argument.") |
|
368 | 364 | kwargs['block'] = True |
|
369 | 365 | return self.map(f,*sequences,**kwargs) |
|
370 | 366 | |
|
371 | 367 | def imap(self, f, *sequences, **kwargs): |
|
372 | """Parallel version of `itertools.imap`. | |
|
368 | """Parallel version of :func:`itertools.imap`. | |
|
373 | 369 | |
|
374 | 370 | See `self.map` for details. |
|
375 | 371 | |
|
376 | 372 | """ |
|
377 | 373 | |
|
378 | 374 | return iter(self.map_async(f,*sequences, **kwargs)) |
|
379 | 375 | |
|
380 | 376 | #------------------------------------------------------------------- |
|
381 | 377 | # Decorators |
|
382 | 378 | #------------------------------------------------------------------- |
|
383 | 379 | |
|
384 | 380 | def remote(self, block=None, **flags): |
|
385 | 381 | """Decorator for making a RemoteFunction""" |
|
386 | 382 | block = self.block if block is None else block |
|
387 | 383 | return remote(self, block=block, **flags) |
|
388 | 384 | |
|
389 | 385 | def parallel(self, dist='b', block=None, **flags): |
|
390 | 386 | """Decorator for making a ParallelFunction""" |
|
391 | 387 | block = self.block if block is None else block |
|
392 | 388 | return parallel(self, dist=dist, block=block, **flags) |
|
393 | 389 | |
|
394 | 390 | @skip_doctest |
|
395 | 391 | class DirectView(View): |
|
396 | 392 | """Direct Multiplexer View of one or more engines. |
|
397 | 393 | |
|
398 | 394 | These are created via indexed access to a client: |
|
399 | 395 | |
|
400 | 396 | >>> dv_1 = client[1] |
|
401 | 397 | >>> dv_all = client[:] |
|
402 | 398 | >>> dv_even = client[::2] |
|
403 | 399 | >>> dv_some = client[1:3] |
|
404 | 400 | |
|
405 | 401 | This object provides dictionary access to engine namespaces: |
|
406 | 402 | |
|
407 | 403 | # push a=5: |
|
408 | 404 | >>> dv['a'] = 5 |
|
409 | 405 | # pull 'foo': |
|
410 | 406 | >>> db['foo'] |
|
411 | 407 | |
|
412 | 408 | """ |
|
413 | 409 | |
|
414 | 410 | def __init__(self, client=None, socket=None, targets=None): |
|
415 | 411 | super(DirectView, self).__init__(client=client, socket=socket, targets=targets) |
|
416 | 412 | |
|
417 | 413 | @property |
|
418 | 414 | def importer(self): |
|
419 | 415 | """sync_imports(local=True) as a property. |
|
420 | 416 | |
|
421 | 417 | See sync_imports for details. |
|
422 | 418 | |
|
423 | 419 | """ |
|
424 | 420 | return self.sync_imports(True) |
|
425 | 421 | |
|
426 | 422 | @contextmanager |
|
427 | 423 | def sync_imports(self, local=True, quiet=False): |
|
428 | 424 | """Context Manager for performing simultaneous local and remote imports. |
|
429 | 425 | |
|
430 | 426 | 'import x as y' will *not* work. The 'as y' part will simply be ignored. |
|
431 | 427 | |
|
432 | 428 | If `local=True`, then the package will also be imported locally. |
|
433 | 429 | |
|
434 | 430 | If `quiet=True`, no output will be produced when attempting remote |
|
435 | 431 | imports. |
|
436 | 432 | |
|
437 | 433 | Note that remote-only (`local=False`) imports have not been implemented. |
|
438 | 434 | |
|
439 | 435 | >>> with view.sync_imports(): |
|
440 | 436 | ... from numpy import recarray |
|
441 | 437 | importing recarray from numpy on engine(s) |
|
442 | 438 | |
|
443 | 439 | """ |
|
444 | 440 | from IPython.utils.py3compat import builtin_mod |
|
445 | 441 | local_import = builtin_mod.__import__ |
|
446 | 442 | modules = set() |
|
447 | 443 | results = [] |
|
448 | 444 | @util.interactive |
|
449 | 445 | def remote_import(name, fromlist, level): |
|
450 | 446 | """the function to be passed to apply, that actually performs the import |
|
451 | 447 | on the engine, and loads up the user namespace. |
|
452 | 448 | """ |
|
453 | 449 | import sys |
|
454 | 450 | user_ns = globals() |
|
455 | 451 | mod = __import__(name, fromlist=fromlist, level=level) |
|
456 | 452 | if fromlist: |
|
457 | 453 | for key in fromlist: |
|
458 | 454 | user_ns[key] = getattr(mod, key) |
|
459 | 455 | else: |
|
460 | 456 | user_ns[name] = sys.modules[name] |
|
461 | 457 | |
|
462 | 458 | def view_import(name, globals={}, locals={}, fromlist=[], level=0): |
|
463 | 459 | """the drop-in replacement for __import__, that optionally imports |
|
464 | 460 | locally as well. |
|
465 | 461 | """ |
|
466 | 462 | # don't override nested imports |
|
467 | 463 | save_import = builtin_mod.__import__ |
|
468 | 464 | builtin_mod.__import__ = local_import |
|
469 | 465 | |
|
470 | 466 | if imp.lock_held(): |
|
471 | 467 | # this is a side-effect import, don't do it remotely, or even |
|
472 | 468 | # ignore the local effects |
|
473 | 469 | return local_import(name, globals, locals, fromlist, level) |
|
474 | 470 | |
|
475 | 471 | imp.acquire_lock() |
|
476 | 472 | if local: |
|
477 | 473 | mod = local_import(name, globals, locals, fromlist, level) |
|
478 | 474 | else: |
|
479 | 475 | raise NotImplementedError("remote-only imports not yet implemented") |
|
480 | 476 | imp.release_lock() |
|
481 | 477 | |
|
482 | 478 | key = name+':'+','.join(fromlist or []) |
|
483 | 479 | if level <= 0 and key not in modules: |
|
484 | 480 | modules.add(key) |
|
485 | 481 | if not quiet: |
|
486 | 482 | if fromlist: |
|
487 | 483 | print("importing %s from %s on engine(s)"%(','.join(fromlist), name)) |
|
488 | 484 | else: |
|
489 | 485 | print("importing %s on engine(s)"%name) |
|
490 | 486 | results.append(self.apply_async(remote_import, name, fromlist, level)) |
|
491 | 487 | # restore override |
|
492 | 488 | builtin_mod.__import__ = save_import |
|
493 | 489 | |
|
494 | 490 | return mod |
|
495 | 491 | |
|
496 | 492 | # override __import__ |
|
497 | 493 | builtin_mod.__import__ = view_import |
|
498 | 494 | try: |
|
499 | 495 | # enter the block |
|
500 | 496 | yield |
|
501 | 497 | except ImportError: |
|
502 | 498 | if local: |
|
503 | 499 | raise |
|
504 | 500 | else: |
|
505 | 501 | # ignore import errors if not doing local imports |
|
506 | 502 | pass |
|
507 | 503 | finally: |
|
508 | 504 | # always restore __import__ |
|
509 | 505 | builtin_mod.__import__ = local_import |
|
510 | 506 | |
|
511 | 507 | for r in results: |
|
512 | 508 | # raise possible remote ImportErrors here |
|
513 | 509 | r.get() |
|
514 | 510 | |
|
515 | 511 | |
|
516 | 512 | @sync_results |
|
517 | 513 | @save_ids |
|
518 | 514 | def _really_apply(self, f, args=None, kwargs=None, targets=None, block=None, track=None): |
|
519 | 515 | """calls f(*args, **kwargs) on remote engines, returning the result. |
|
520 | 516 | |
|
521 | 517 | This method sets all of `apply`'s flags via this View's attributes. |
|
522 | 518 | |
|
523 | 519 | Parameters |
|
524 | 520 | ---------- |
|
525 | 521 | |
|
526 | 522 | f : callable |
|
527 | 523 | |
|
528 | 524 | args : list [default: empty] |
|
529 | 525 | |
|
530 | 526 | kwargs : dict [default: empty] |
|
531 | 527 | |
|
532 | 528 | targets : target list [default: self.targets] |
|
533 | 529 | where to run |
|
534 | 530 | block : bool [default: self.block] |
|
535 | 531 | whether to block |
|
536 | 532 | track : bool [default: self.track] |
|
537 | 533 | whether to ask zmq to track the message, for safe non-copying sends |
|
538 | 534 | |
|
539 | 535 | Returns |
|
540 | 536 | ------- |
|
541 | 537 | |
|
542 | 538 | if self.block is False: |
|
543 | 539 | returns AsyncResult |
|
544 | 540 | else: |
|
545 | 541 | returns actual result of f(*args, **kwargs) on the engine(s) |
|
546 | 542 | This will be a list of self.targets is also a list (even length 1), or |
|
547 | 543 | the single result if self.targets is an integer engine id |
|
548 | 544 | """ |
|
549 | 545 | args = [] if args is None else args |
|
550 | 546 | kwargs = {} if kwargs is None else kwargs |
|
551 | 547 | block = self.block if block is None else block |
|
552 | 548 | track = self.track if track is None else track |
|
553 | 549 | targets = self.targets if targets is None else targets |
|
554 | 550 | |
|
555 | 551 | _idents, _targets = self.client._build_targets(targets) |
|
556 | 552 | msg_ids = [] |
|
557 | 553 | trackers = [] |
|
558 | 554 | for ident in _idents: |
|
559 | 555 | msg = self.client.send_apply_request(self._socket, f, args, kwargs, track=track, |
|
560 | 556 | ident=ident) |
|
561 | 557 | if track: |
|
562 | 558 | trackers.append(msg['tracker']) |
|
563 | 559 | msg_ids.append(msg['header']['msg_id']) |
|
564 | 560 | if isinstance(targets, int): |
|
565 | 561 | msg_ids = msg_ids[0] |
|
566 | 562 | tracker = None if track is False else zmq.MessageTracker(*trackers) |
|
567 | 563 | ar = AsyncResult(self.client, msg_ids, fname=getname(f), targets=_targets, tracker=tracker) |
|
568 | 564 | if block: |
|
569 | 565 | try: |
|
570 | 566 | return ar.get() |
|
571 | 567 | except KeyboardInterrupt: |
|
572 | 568 | pass |
|
573 | 569 | return ar |
|
574 | 570 | |
|
575 | 571 | |
|
576 | 572 | @sync_results |
|
577 | 573 | def map(self, f, *sequences, **kwargs): |
|
578 | """view.map(f, *sequences, block=self.block) => list|AsyncMapResult | |
|
574 | """``view.map(f, *sequences, block=self.block)`` => list|AsyncMapResult | |
|
579 | 575 | |
|
580 | 576 | Parallel version of builtin `map`, using this View's `targets`. |
|
581 | 577 | |
|
582 | 578 | There will be one task per target, so work will be chunked |
|
583 | 579 | if the sequences are longer than `targets`. |
|
584 | 580 | |
|
585 | 581 | Results can be iterated as they are ready, but will become available in chunks. |
|
586 | 582 | |
|
587 | 583 | Parameters |
|
588 | 584 | ---------- |
|
589 | 585 | |
|
590 | 586 | f : callable |
|
591 | 587 | function to be mapped |
|
592 | 588 | *sequences: one or more sequences of matching length |
|
593 | 589 | the sequences to be distributed and passed to `f` |
|
594 | 590 | block : bool |
|
595 | 591 | whether to wait for the result or not [default self.block] |
|
596 | 592 | |
|
597 | 593 | Returns |
|
598 | 594 | ------- |
|
599 | 595 | |
|
600 | if block=False: | |
|
601 | AsyncMapResult | |
|
602 | An object like AsyncResult, but which reassembles the sequence of results | |
|
603 | into a single list. AsyncMapResults can be iterated through before all | |
|
604 | results are complete. | |
|
605 | else: | |
|
606 |
|
|
|
607 |
|
|
|
596 | ||
|
597 | If block=False | |
|
598 | An :class:`~IPython.parallel.client.asyncresult.AsyncMapResult` instance. | |
|
599 | An object like AsyncResult, but which reassembles the sequence of results | |
|
600 | into a single list. AsyncMapResults can be iterated through before all | |
|
601 | results are complete. | |
|
602 | else | |
|
603 | A list, the result of ``map(f,*sequences)`` | |
|
608 | 604 | """ |
|
609 | 605 | |
|
610 | 606 | block = kwargs.pop('block', self.block) |
|
611 | 607 | for k in kwargs.keys(): |
|
612 | 608 | if k not in ['block', 'track']: |
|
613 | 609 | raise TypeError("invalid keyword arg, %r"%k) |
|
614 | 610 | |
|
615 | 611 | assert len(sequences) > 0, "must have some sequences to map onto!" |
|
616 | 612 | pf = ParallelFunction(self, f, block=block, **kwargs) |
|
617 | 613 | return pf.map(*sequences) |
|
618 | 614 | |
|
619 | 615 | @sync_results |
|
620 | 616 | @save_ids |
|
621 | 617 | def execute(self, code, silent=True, targets=None, block=None): |
|
622 | 618 | """Executes `code` on `targets` in blocking or nonblocking manner. |
|
623 | 619 | |
|
624 | 620 | ``execute`` is always `bound` (affects engine namespace) |
|
625 | 621 | |
|
626 | 622 | Parameters |
|
627 | 623 | ---------- |
|
628 | 624 | |
|
629 | 625 | code : str |
|
630 | 626 | the code string to be executed |
|
631 | 627 | block : bool |
|
632 | 628 | whether or not to wait until done to return |
|
633 | 629 | default: self.block |
|
634 | 630 | """ |
|
635 | 631 | block = self.block if block is None else block |
|
636 | 632 | targets = self.targets if targets is None else targets |
|
637 | 633 | |
|
638 | 634 | _idents, _targets = self.client._build_targets(targets) |
|
639 | 635 | msg_ids = [] |
|
640 | 636 | trackers = [] |
|
641 | 637 | for ident in _idents: |
|
642 | 638 | msg = self.client.send_execute_request(self._socket, code, silent=silent, ident=ident) |
|
643 | 639 | msg_ids.append(msg['header']['msg_id']) |
|
644 | 640 | if isinstance(targets, int): |
|
645 | 641 | msg_ids = msg_ids[0] |
|
646 | 642 | ar = AsyncResult(self.client, msg_ids, fname='execute', targets=_targets) |
|
647 | 643 | if block: |
|
648 | 644 | try: |
|
649 | 645 | ar.get() |
|
650 | 646 | except KeyboardInterrupt: |
|
651 | 647 | pass |
|
652 | 648 | return ar |
|
653 | 649 | |
|
654 | 650 | def run(self, filename, targets=None, block=None): |
|
655 | 651 | """Execute contents of `filename` on my engine(s). |
|
656 | 652 | |
|
657 | 653 | This simply reads the contents of the file and calls `execute`. |
|
658 | 654 | |
|
659 | 655 | Parameters |
|
660 | 656 | ---------- |
|
661 | 657 | |
|
662 | 658 | filename : str |
|
663 | 659 | The path to the file |
|
664 | 660 | targets : int/str/list of ints/strs |
|
665 | 661 | the engines on which to execute |
|
666 | 662 | default : all |
|
667 | 663 | block : bool |
|
668 | 664 | whether or not to wait until done |
|
669 | 665 | default: self.block |
|
670 | 666 | |
|
671 | 667 | """ |
|
672 | 668 | with open(filename, 'r') as f: |
|
673 | 669 | # add newline in case of trailing indented whitespace |
|
674 | 670 | # which will cause SyntaxError |
|
675 | 671 | code = f.read()+'\n' |
|
676 | 672 | return self.execute(code, block=block, targets=targets) |
|
677 | 673 | |
|
678 | 674 | def update(self, ns): |
|
679 | 675 | """update remote namespace with dict `ns` |
|
680 | 676 | |
|
681 | 677 | See `push` for details. |
|
682 | 678 | """ |
|
683 | 679 | return self.push(ns, block=self.block, track=self.track) |
|
684 | 680 | |
|
685 | 681 | def push(self, ns, targets=None, block=None, track=None): |
|
686 | 682 | """update remote namespace with dict `ns` |
|
687 | 683 | |
|
688 | 684 | Parameters |
|
689 | 685 | ---------- |
|
690 | 686 | |
|
691 | 687 | ns : dict |
|
692 | 688 | dict of keys with which to update engine namespace(s) |
|
693 | 689 | block : bool [default : self.block] |
|
694 | 690 | whether to wait to be notified of engine receipt |
|
695 | 691 | |
|
696 | 692 | """ |
|
697 | 693 | |
|
698 | 694 | block = block if block is not None else self.block |
|
699 | 695 | track = track if track is not None else self.track |
|
700 | 696 | targets = targets if targets is not None else self.targets |
|
701 | 697 | # applier = self.apply_sync if block else self.apply_async |
|
702 | 698 | if not isinstance(ns, dict): |
|
703 | 699 | raise TypeError("Must be a dict, not %s"%type(ns)) |
|
704 | 700 | return self._really_apply(util._push, kwargs=ns, block=block, track=track, targets=targets) |
|
705 | 701 | |
|
706 | 702 | def get(self, key_s): |
|
707 | 703 | """get object(s) by `key_s` from remote namespace |
|
708 | 704 | |
|
709 | 705 | see `pull` for details. |
|
710 | 706 | """ |
|
711 | 707 | # block = block if block is not None else self.block |
|
712 | 708 | return self.pull(key_s, block=True) |
|
713 | 709 | |
|
714 | 710 | def pull(self, names, targets=None, block=None): |
|
715 | 711 | """get object(s) by `name` from remote namespace |
|
716 | 712 | |
|
717 | 713 | will return one object if it is a key. |
|
718 | 714 | can also take a list of keys, in which case it will return a list of objects. |
|
719 | 715 | """ |
|
720 | 716 | block = block if block is not None else self.block |
|
721 | 717 | targets = targets if targets is not None else self.targets |
|
722 | 718 | applier = self.apply_sync if block else self.apply_async |
|
723 | 719 | if isinstance(names, string_types): |
|
724 | 720 | pass |
|
725 | 721 | elif isinstance(names, (list,tuple,set)): |
|
726 | 722 | for key in names: |
|
727 | 723 | if not isinstance(key, string_types): |
|
728 | 724 | raise TypeError("keys must be str, not type %r"%type(key)) |
|
729 | 725 | else: |
|
730 | 726 | raise TypeError("names must be strs, not %r"%names) |
|
731 | 727 | return self._really_apply(util._pull, (names,), block=block, targets=targets) |
|
732 | 728 | |
|
733 | 729 | def scatter(self, key, seq, dist='b', flatten=False, targets=None, block=None, track=None): |
|
734 | 730 | """ |
|
735 | 731 | Partition a Python sequence and send the partitions to a set of engines. |
|
736 | 732 | """ |
|
737 | 733 | block = block if block is not None else self.block |
|
738 | 734 | track = track if track is not None else self.track |
|
739 | 735 | targets = targets if targets is not None else self.targets |
|
740 | 736 | |
|
741 | 737 | # construct integer ID list: |
|
742 | 738 | targets = self.client._build_targets(targets)[1] |
|
743 | 739 | |
|
744 | 740 | mapObject = Map.dists[dist]() |
|
745 | 741 | nparts = len(targets) |
|
746 | 742 | msg_ids = [] |
|
747 | 743 | trackers = [] |
|
748 | 744 | for index, engineid in enumerate(targets): |
|
749 | 745 | partition = mapObject.getPartition(seq, index, nparts) |
|
750 | 746 | if flatten and len(partition) == 1: |
|
751 | 747 | ns = {key: partition[0]} |
|
752 | 748 | else: |
|
753 | 749 | ns = {key: partition} |
|
754 | 750 | r = self.push(ns, block=False, track=track, targets=engineid) |
|
755 | 751 | msg_ids.extend(r.msg_ids) |
|
756 | 752 | if track: |
|
757 | 753 | trackers.append(r._tracker) |
|
758 | 754 | |
|
759 | 755 | if track: |
|
760 | 756 | tracker = zmq.MessageTracker(*trackers) |
|
761 | 757 | else: |
|
762 | 758 | tracker = None |
|
763 | 759 | |
|
764 | 760 | r = AsyncResult(self.client, msg_ids, fname='scatter', targets=targets, tracker=tracker) |
|
765 | 761 | if block: |
|
766 | 762 | r.wait() |
|
767 | 763 | else: |
|
768 | 764 | return r |
|
769 | 765 | |
|
770 | 766 | @sync_results |
|
771 | 767 | @save_ids |
|
772 | 768 | def gather(self, key, dist='b', targets=None, block=None): |
|
773 | 769 | """ |
|
774 | 770 | Gather a partitioned sequence on a set of engines as a single local seq. |
|
775 | 771 | """ |
|
776 | 772 | block = block if block is not None else self.block |
|
777 | 773 | targets = targets if targets is not None else self.targets |
|
778 | 774 | mapObject = Map.dists[dist]() |
|
779 | 775 | msg_ids = [] |
|
780 | 776 | |
|
781 | 777 | # construct integer ID list: |
|
782 | 778 | targets = self.client._build_targets(targets)[1] |
|
783 | 779 | |
|
784 | 780 | for index, engineid in enumerate(targets): |
|
785 | 781 | msg_ids.extend(self.pull(key, block=False, targets=engineid).msg_ids) |
|
786 | 782 | |
|
787 | 783 | r = AsyncMapResult(self.client, msg_ids, mapObject, fname='gather') |
|
788 | 784 | |
|
789 | 785 | if block: |
|
790 | 786 | try: |
|
791 | 787 | return r.get() |
|
792 | 788 | except KeyboardInterrupt: |
|
793 | 789 | pass |
|
794 | 790 | return r |
|
795 | 791 | |
|
796 | 792 | def __getitem__(self, key): |
|
797 | 793 | return self.get(key) |
|
798 | 794 | |
|
799 | 795 | def __setitem__(self,key, value): |
|
800 | 796 | self.update({key:value}) |
|
801 | 797 | |
|
802 | 798 | def clear(self, targets=None, block=None): |
|
803 | 799 | """Clear the remote namespaces on my engines.""" |
|
804 | 800 | block = block if block is not None else self.block |
|
805 | 801 | targets = targets if targets is not None else self.targets |
|
806 | 802 | return self.client.clear(targets=targets, block=block) |
|
807 | 803 | |
|
808 | 804 | #---------------------------------------- |
|
809 | 805 | # activate for %px, %autopx, etc. magics |
|
810 | 806 | #---------------------------------------- |
|
811 | 807 | |
|
812 | 808 | def activate(self, suffix=''): |
|
813 | 809 | """Activate IPython magics associated with this View |
|
814 | 810 | |
|
815 | 811 | Defines the magics `%px, %autopx, %pxresult, %%px, %pxconfig` |
|
816 | 812 | |
|
817 | 813 | Parameters |
|
818 | 814 | ---------- |
|
819 | 815 | |
|
820 | 816 | suffix: str [default: ''] |
|
821 | 817 | The suffix, if any, for the magics. This allows you to have |
|
822 | 818 | multiple views associated with parallel magics at the same time. |
|
823 | 819 | |
|
824 | 820 | e.g. ``rc[::2].activate(suffix='_even')`` will give you |
|
825 | 821 | the magics ``%px_even``, ``%pxresult_even``, etc. for running magics |
|
826 | 822 | on the even engines. |
|
827 | 823 | """ |
|
828 | 824 | |
|
829 | 825 | from IPython.parallel.client.magics import ParallelMagics |
|
830 | 826 | |
|
831 | 827 | try: |
|
832 | 828 | # This is injected into __builtins__. |
|
833 | 829 | ip = get_ipython() |
|
834 | 830 | except NameError: |
|
835 | 831 | print("The IPython parallel magics (%px, etc.) only work within IPython.") |
|
836 | 832 | return |
|
837 | 833 | |
|
838 | 834 | M = ParallelMagics(ip, self, suffix) |
|
839 | 835 | ip.magics_manager.register(M) |
|
840 | 836 | |
|
841 | 837 | |
|
842 | 838 | @skip_doctest |
|
843 | 839 | class LoadBalancedView(View): |
|
844 | 840 | """An load-balancing View that only executes via the Task scheduler. |
|
845 | 841 | |
|
846 | 842 | Load-balanced views can be created with the client's `view` method: |
|
847 | 843 | |
|
848 | 844 | >>> v = client.load_balanced_view() |
|
849 | 845 | |
|
850 | 846 | or targets can be specified, to restrict the potential destinations: |
|
851 | 847 | |
|
852 | 848 | >>> v = client.client.load_balanced_view([1,3]) |
|
853 | 849 | |
|
854 | 850 | which would restrict loadbalancing to between engines 1 and 3. |
|
855 | 851 | |
|
856 | 852 | """ |
|
857 | 853 | |
|
858 | 854 | follow=Any() |
|
859 | 855 | after=Any() |
|
860 | 856 | timeout=CFloat() |
|
861 | 857 | retries = Integer(0) |
|
862 | 858 | |
|
863 | 859 | _task_scheme = Any() |
|
864 | 860 | _flag_names = List(['targets', 'block', 'track', 'follow', 'after', 'timeout', 'retries']) |
|
865 | 861 | |
|
866 | 862 | def __init__(self, client=None, socket=None, **flags): |
|
867 | 863 | super(LoadBalancedView, self).__init__(client=client, socket=socket, **flags) |
|
868 | 864 | self._task_scheme=client._task_scheme |
|
869 | 865 | |
|
870 | 866 | def _validate_dependency(self, dep): |
|
871 | 867 | """validate a dependency. |
|
872 | 868 | |
|
873 | 869 | For use in `set_flags`. |
|
874 | 870 | """ |
|
875 | 871 | if dep is None or isinstance(dep, string_types + (AsyncResult, Dependency)): |
|
876 | 872 | return True |
|
877 | 873 | elif isinstance(dep, (list,set, tuple)): |
|
878 | 874 | for d in dep: |
|
879 | 875 | if not isinstance(d, string_types + (AsyncResult,)): |
|
880 | 876 | return False |
|
881 | 877 | elif isinstance(dep, dict): |
|
882 | 878 | if set(dep.keys()) != set(Dependency().as_dict().keys()): |
|
883 | 879 | return False |
|
884 | 880 | if not isinstance(dep['msg_ids'], list): |
|
885 | 881 | return False |
|
886 | 882 | for d in dep['msg_ids']: |
|
887 | 883 | if not isinstance(d, string_types): |
|
888 | 884 | return False |
|
889 | 885 | else: |
|
890 | 886 | return False |
|
891 | 887 | |
|
892 | 888 | return True |
|
893 | 889 | |
|
894 | 890 | def _render_dependency(self, dep): |
|
895 | 891 | """helper for building jsonable dependencies from various input forms.""" |
|
896 | 892 | if isinstance(dep, Dependency): |
|
897 | 893 | return dep.as_dict() |
|
898 | 894 | elif isinstance(dep, AsyncResult): |
|
899 | 895 | return dep.msg_ids |
|
900 | 896 | elif dep is None: |
|
901 | 897 | return [] |
|
902 | 898 | else: |
|
903 | 899 | # pass to Dependency constructor |
|
904 | 900 | return list(Dependency(dep)) |
|
905 | 901 | |
|
906 | 902 | def set_flags(self, **kwargs): |
|
907 | 903 | """set my attribute flags by keyword. |
|
908 | 904 | |
|
909 | 905 | A View is a wrapper for the Client's apply method, but with attributes |
|
910 | 906 | that specify keyword arguments, those attributes can be set by keyword |
|
911 | 907 | argument with this method. |
|
912 | 908 | |
|
913 | 909 | Parameters |
|
914 | 910 | ---------- |
|
915 | 911 | |
|
916 | 912 | block : bool |
|
917 | 913 | whether to wait for results |
|
918 | 914 | track : bool |
|
919 | 915 | whether to create a MessageTracker to allow the user to |
|
920 | 916 | safely edit after arrays and buffers during non-copying |
|
921 | 917 | sends. |
|
922 | 918 | |
|
923 | 919 | after : Dependency or collection of msg_ids |
|
924 | 920 | Only for load-balanced execution (targets=None) |
|
925 | 921 | Specify a list of msg_ids as a time-based dependency. |
|
926 | 922 | This job will only be run *after* the dependencies |
|
927 | 923 | have been met. |
|
928 | 924 | |
|
929 | 925 | follow : Dependency or collection of msg_ids |
|
930 | 926 | Only for load-balanced execution (targets=None) |
|
931 | 927 | Specify a list of msg_ids as a location-based dependency. |
|
932 | 928 | This job will only be run on an engine where this dependency |
|
933 | 929 | is met. |
|
934 | 930 | |
|
935 | 931 | timeout : float/int or None |
|
936 | 932 | Only for load-balanced execution (targets=None) |
|
937 | 933 | Specify an amount of time (in seconds) for the scheduler to |
|
938 | 934 | wait for dependencies to be met before failing with a |
|
939 | 935 | DependencyTimeout. |
|
940 | 936 | |
|
941 | 937 | retries : int |
|
942 | 938 | Number of times a task will be retried on failure. |
|
943 | 939 | """ |
|
944 | 940 | |
|
945 | 941 | super(LoadBalancedView, self).set_flags(**kwargs) |
|
946 | 942 | for name in ('follow', 'after'): |
|
947 | 943 | if name in kwargs: |
|
948 | 944 | value = kwargs[name] |
|
949 | 945 | if self._validate_dependency(value): |
|
950 | 946 | setattr(self, name, value) |
|
951 | 947 | else: |
|
952 | 948 | raise ValueError("Invalid dependency: %r"%value) |
|
953 | 949 | if 'timeout' in kwargs: |
|
954 | 950 | t = kwargs['timeout'] |
|
955 | 951 | if not isinstance(t, (int, float, type(None))): |
|
956 | 952 | if (not PY3) and (not isinstance(t, long)): |
|
957 | 953 | raise TypeError("Invalid type for timeout: %r"%type(t)) |
|
958 | 954 | if t is not None: |
|
959 | 955 | if t < 0: |
|
960 | 956 | raise ValueError("Invalid timeout: %s"%t) |
|
961 | 957 | self.timeout = t |
|
962 | 958 | |
|
963 | 959 | @sync_results |
|
964 | 960 | @save_ids |
|
965 | 961 | def _really_apply(self, f, args=None, kwargs=None, block=None, track=None, |
|
966 | 962 | after=None, follow=None, timeout=None, |
|
967 | 963 | targets=None, retries=None): |
|
968 | 964 | """calls f(*args, **kwargs) on a remote engine, returning the result. |
|
969 | 965 | |
|
970 | 966 | This method temporarily sets all of `apply`'s flags for a single call. |
|
971 | 967 | |
|
972 | 968 | Parameters |
|
973 | 969 | ---------- |
|
974 | 970 | |
|
975 | 971 | f : callable |
|
976 | 972 | |
|
977 | 973 | args : list [default: empty] |
|
978 | 974 | |
|
979 | 975 | kwargs : dict [default: empty] |
|
980 | 976 | |
|
981 | 977 | block : bool [default: self.block] |
|
982 | 978 | whether to block |
|
983 | 979 | track : bool [default: self.track] |
|
984 | 980 | whether to ask zmq to track the message, for safe non-copying sends |
|
985 | 981 | |
|
986 | 982 | !!!!!! TODO: THE REST HERE !!!! |
|
987 | 983 | |
|
988 | 984 | Returns |
|
989 | 985 | ------- |
|
990 | 986 | |
|
991 | 987 | if self.block is False: |
|
992 | 988 | returns AsyncResult |
|
993 | 989 | else: |
|
994 | 990 | returns actual result of f(*args, **kwargs) on the engine(s) |
|
995 | 991 | This will be a list of self.targets is also a list (even length 1), or |
|
996 | 992 | the single result if self.targets is an integer engine id |
|
997 | 993 | """ |
|
998 | 994 | |
|
999 | 995 | # validate whether we can run |
|
1000 | 996 | if self._socket.closed: |
|
1001 | 997 | msg = "Task farming is disabled" |
|
1002 | 998 | if self._task_scheme == 'pure': |
|
1003 | 999 | msg += " because the pure ZMQ scheduler cannot handle" |
|
1004 | 1000 | msg += " disappearing engines." |
|
1005 | 1001 | raise RuntimeError(msg) |
|
1006 | 1002 | |
|
1007 | 1003 | if self._task_scheme == 'pure': |
|
1008 | 1004 | # pure zmq scheme doesn't support extra features |
|
1009 | 1005 | msg = "Pure ZMQ scheduler doesn't support the following flags:" |
|
1010 | 1006 | "follow, after, retries, targets, timeout" |
|
1011 | 1007 | if (follow or after or retries or targets or timeout): |
|
1012 | 1008 | # hard fail on Scheduler flags |
|
1013 | 1009 | raise RuntimeError(msg) |
|
1014 | 1010 | if isinstance(f, dependent): |
|
1015 | 1011 | # soft warn on functional dependencies |
|
1016 | 1012 | warnings.warn(msg, RuntimeWarning) |
|
1017 | 1013 | |
|
1018 | 1014 | # build args |
|
1019 | 1015 | args = [] if args is None else args |
|
1020 | 1016 | kwargs = {} if kwargs is None else kwargs |
|
1021 | 1017 | block = self.block if block is None else block |
|
1022 | 1018 | track = self.track if track is None else track |
|
1023 | 1019 | after = self.after if after is None else after |
|
1024 | 1020 | retries = self.retries if retries is None else retries |
|
1025 | 1021 | follow = self.follow if follow is None else follow |
|
1026 | 1022 | timeout = self.timeout if timeout is None else timeout |
|
1027 | 1023 | targets = self.targets if targets is None else targets |
|
1028 | 1024 | |
|
1029 | 1025 | if not isinstance(retries, int): |
|
1030 | 1026 | raise TypeError('retries must be int, not %r'%type(retries)) |
|
1031 | 1027 | |
|
1032 | 1028 | if targets is None: |
|
1033 | 1029 | idents = [] |
|
1034 | 1030 | else: |
|
1035 | 1031 | idents = self.client._build_targets(targets)[0] |
|
1036 | 1032 | # ensure *not* bytes |
|
1037 | 1033 | idents = [ ident.decode() for ident in idents ] |
|
1038 | 1034 | |
|
1039 | 1035 | after = self._render_dependency(after) |
|
1040 | 1036 | follow = self._render_dependency(follow) |
|
1041 | 1037 | metadata = dict(after=after, follow=follow, timeout=timeout, targets=idents, retries=retries) |
|
1042 | 1038 | |
|
1043 | 1039 | msg = self.client.send_apply_request(self._socket, f, args, kwargs, track=track, |
|
1044 | 1040 | metadata=metadata) |
|
1045 | 1041 | tracker = None if track is False else msg['tracker'] |
|
1046 | 1042 | |
|
1047 | 1043 | ar = AsyncResult(self.client, msg['header']['msg_id'], fname=getname(f), targets=None, tracker=tracker) |
|
1048 | 1044 | |
|
1049 | 1045 | if block: |
|
1050 | 1046 | try: |
|
1051 | 1047 | return ar.get() |
|
1052 | 1048 | except KeyboardInterrupt: |
|
1053 | 1049 | pass |
|
1054 | 1050 | return ar |
|
1055 | 1051 | |
|
1056 | 1052 | @sync_results |
|
1057 | 1053 | @save_ids |
|
1058 | 1054 | def map(self, f, *sequences, **kwargs): |
|
1059 | """view.map(f, *sequences, block=self.block, chunksize=1, ordered=True) => list|AsyncMapResult | |
|
1055 | """``view.map(f, *sequences, block=self.block, chunksize=1, ordered=True)`` => list|AsyncMapResult | |
|
1060 | 1056 | |
|
1061 | 1057 | Parallel version of builtin `map`, load-balanced by this View. |
|
1062 | 1058 | |
|
1063 | 1059 | `block`, and `chunksize` can be specified by keyword only. |
|
1064 | 1060 | |
|
1065 | 1061 | Each `chunksize` elements will be a separate task, and will be |
|
1066 | 1062 | load-balanced. This lets individual elements be available for iteration |
|
1067 | 1063 | as soon as they arrive. |
|
1068 | 1064 | |
|
1069 | 1065 | Parameters |
|
1070 | 1066 | ---------- |
|
1071 | 1067 | |
|
1072 | 1068 | f : callable |
|
1073 | 1069 | function to be mapped |
|
1074 | 1070 | *sequences: one or more sequences of matching length |
|
1075 | 1071 | the sequences to be distributed and passed to `f` |
|
1076 | 1072 | block : bool [default self.block] |
|
1077 | 1073 | whether to wait for the result or not |
|
1078 | 1074 | track : bool |
|
1079 | 1075 | whether to create a MessageTracker to allow the user to |
|
1080 | 1076 | safely edit after arrays and buffers during non-copying |
|
1081 | 1077 | sends. |
|
1082 | 1078 | chunksize : int [default 1] |
|
1083 | 1079 | how many elements should be in each task. |
|
1084 | 1080 | ordered : bool [default True] |
|
1085 | 1081 | Whether the results should be gathered as they arrive, or enforce |
|
1086 | 1082 | the order of submission. |
|
1087 | 1083 | |
|
1088 | 1084 | Only applies when iterating through AsyncMapResult as results arrive. |
|
1089 | 1085 | Has no effect when block=True. |
|
1090 | 1086 | |
|
1091 | 1087 | Returns |
|
1092 | 1088 | ------- |
|
1093 | 1089 | |
|
1094 |
if block=False |
|
|
1095 | AsyncMapResult | |
|
1096 |
|
|
|
1097 |
|
|
|
1098 |
|
|
|
1099 |
|
|
|
1100 |
|
|
|
1101 | ||
|
1090 | if block=False | |
|
1091 | An :class:`~IPython.parallel.client.asyncresult.AsyncMapResult` instance. | |
|
1092 | An object like AsyncResult, but which reassembles the sequence of results | |
|
1093 | into a single list. AsyncMapResults can be iterated through before all | |
|
1094 | results are complete. | |
|
1095 | else | |
|
1096 | A list, the result of ``map(f,*sequences)`` | |
|
1102 | 1097 | """ |
|
1103 | 1098 | |
|
1104 | 1099 | # default |
|
1105 | 1100 | block = kwargs.get('block', self.block) |
|
1106 | 1101 | chunksize = kwargs.get('chunksize', 1) |
|
1107 | 1102 | ordered = kwargs.get('ordered', True) |
|
1108 | 1103 | |
|
1109 | 1104 | keyset = set(kwargs.keys()) |
|
1110 | 1105 | extra_keys = keyset.difference_update(set(['block', 'chunksize'])) |
|
1111 | 1106 | if extra_keys: |
|
1112 | 1107 | raise TypeError("Invalid kwargs: %s"%list(extra_keys)) |
|
1113 | 1108 | |
|
1114 | 1109 | assert len(sequences) > 0, "must have some sequences to map onto!" |
|
1115 | 1110 | |
|
1116 | 1111 | pf = ParallelFunction(self, f, block=block, chunksize=chunksize, ordered=ordered) |
|
1117 | 1112 | return pf.map(*sequences) |
|
1118 | 1113 | |
|
1119 | 1114 | __all__ = ['LoadBalancedView', 'DirectView'] |
@@ -1,230 +1,229 b'' | |||
|
1 | 1 | """Dependency utilities |
|
2 | 2 | |
|
3 | 3 | Authors: |
|
4 | 4 | |
|
5 | 5 | * Min RK |
|
6 | 6 | """ |
|
7 | 7 | #----------------------------------------------------------------------------- |
|
8 | 8 | # Copyright (C) 2013 The IPython Development Team |
|
9 | 9 | # |
|
10 | 10 | # Distributed under the terms of the BSD License. The full license is in |
|
11 | 11 | # the file COPYING, distributed as part of this software. |
|
12 | 12 | #----------------------------------------------------------------------------- |
|
13 | 13 | |
|
14 | 14 | from types import ModuleType |
|
15 | 15 | |
|
16 | 16 | from IPython.parallel.client.asyncresult import AsyncResult |
|
17 | 17 | from IPython.parallel.error import UnmetDependency |
|
18 | 18 | from IPython.parallel.util import interactive |
|
19 | 19 | from IPython.utils import py3compat |
|
20 | 20 | from IPython.utils.py3compat import string_types |
|
21 | 21 | from IPython.utils.pickleutil import can, uncan |
|
22 | 22 | |
|
23 | 23 | class depend(object): |
|
24 | 24 | """Dependency decorator, for use with tasks. |
|
25 | 25 | |
|
26 | 26 | `@depend` lets you define a function for engine dependencies |
|
27 | 27 | just like you use `apply` for tasks. |
|
28 | 28 | |
|
29 | 29 | |
|
30 | 30 | Examples |
|
31 | 31 | -------- |
|
32 | 32 | :: |
|
33 | 33 | |
|
34 | 34 | @depend(df, a,b, c=5) |
|
35 | 35 | def f(m,n,p) |
|
36 | 36 | |
|
37 | 37 | view.apply(f, 1,2,3) |
|
38 | 38 | |
|
39 | 39 | will call df(a,b,c=5) on the engine, and if it returns False or |
|
40 | 40 | raises an UnmetDependency error, then the task will not be run |
|
41 | 41 | and another engine will be tried. |
|
42 | 42 | """ |
|
43 | 43 | def __init__(self, f, *args, **kwargs): |
|
44 | 44 | self.f = f |
|
45 | 45 | self.args = args |
|
46 | 46 | self.kwargs = kwargs |
|
47 | 47 | |
|
48 | 48 | def __call__(self, f): |
|
49 | 49 | return dependent(f, self.f, *self.args, **self.kwargs) |
|
50 | 50 | |
|
51 | 51 | class dependent(object): |
|
52 | 52 | """A function that depends on another function. |
|
53 | 53 | This is an object to prevent the closure used |
|
54 | 54 | in traditional decorators, which are not picklable. |
|
55 | 55 | """ |
|
56 | 56 | |
|
57 | 57 | def __init__(self, f, df, *dargs, **dkwargs): |
|
58 | 58 | self.f = f |
|
59 | 59 | name = getattr(f, '__name__', 'f') |
|
60 | 60 | if py3compat.PY3: |
|
61 | 61 | self.__name__ = name |
|
62 | 62 | else: |
|
63 | 63 | self.func_name = name |
|
64 | 64 | self.df = df |
|
65 | 65 | self.dargs = dargs |
|
66 | 66 | self.dkwargs = dkwargs |
|
67 | 67 | |
|
68 | 68 | def check_dependency(self): |
|
69 | 69 | if self.df(*self.dargs, **self.dkwargs) is False: |
|
70 | 70 | raise UnmetDependency() |
|
71 | 71 | |
|
72 | 72 | def __call__(self, *args, **kwargs): |
|
73 | 73 | return self.f(*args, **kwargs) |
|
74 | 74 | |
|
75 | 75 | if not py3compat.PY3: |
|
76 | 76 | @property |
|
77 | 77 | def __name__(self): |
|
78 | 78 | return self.func_name |
|
79 | 79 | |
|
80 | 80 | @interactive |
|
81 | 81 | def _require(*modules, **mapping): |
|
82 | 82 | """Helper for @require decorator.""" |
|
83 | 83 | from IPython.parallel.error import UnmetDependency |
|
84 | 84 | from IPython.utils.pickleutil import uncan |
|
85 | 85 | user_ns = globals() |
|
86 | 86 | for name in modules: |
|
87 | 87 | try: |
|
88 | 88 | exec('import %s' % name, user_ns) |
|
89 | 89 | except ImportError: |
|
90 | 90 | raise UnmetDependency(name) |
|
91 | 91 | |
|
92 | 92 | for name, cobj in mapping.items(): |
|
93 | 93 | user_ns[name] = uncan(cobj, user_ns) |
|
94 | 94 | return True |
|
95 | 95 | |
|
96 | 96 | def require(*objects, **mapping): |
|
97 | 97 | """Simple decorator for requiring local objects and modules to be available |
|
98 | 98 | when the decorated function is called on the engine. |
|
99 | 99 | |
|
100 | 100 | Modules specified by name or passed directly will be imported |
|
101 | 101 | prior to calling the decorated function. |
|
102 | 102 | |
|
103 | 103 | Objects other than modules will be pushed as a part of the task. |
|
104 | 104 | Functions can be passed positionally, |
|
105 | 105 | and will be pushed to the engine with their __name__. |
|
106 | 106 | Other objects can be passed by keyword arg. |
|
107 | 107 | |
|
108 | Examples | |
|
109 | -------- | |
|
108 | Examples:: | |
|
110 | 109 | |
|
111 | In [1]: @require('numpy') | |
|
112 | ...: def norm(a): | |
|
113 | ...: return numpy.linalg.norm(a,2) | |
|
110 | In [1]: @require('numpy') | |
|
111 | ...: def norm(a): | |
|
112 | ...: return numpy.linalg.norm(a,2) | |
|
114 | 113 | |
|
115 | In [2]: foo = lambda x: x*x | |
|
116 | In [3]: @require(foo) | |
|
117 | ...: def bar(a): | |
|
118 | ...: return foo(1-a) | |
|
114 | In [2]: foo = lambda x: x*x | |
|
115 | In [3]: @require(foo) | |
|
116 | ...: def bar(a): | |
|
117 | ...: return foo(1-a) | |
|
119 | 118 | """ |
|
120 | 119 | names = [] |
|
121 | 120 | for obj in objects: |
|
122 | 121 | if isinstance(obj, ModuleType): |
|
123 | 122 | obj = obj.__name__ |
|
124 | 123 | |
|
125 | 124 | if isinstance(obj, string_types): |
|
126 | 125 | names.append(obj) |
|
127 | 126 | elif hasattr(obj, '__name__'): |
|
128 | 127 | mapping[obj.__name__] = obj |
|
129 | 128 | else: |
|
130 | 129 | raise TypeError("Objects other than modules and functions " |
|
131 | 130 | "must be passed by kwarg, but got: %s" % type(obj) |
|
132 | 131 | ) |
|
133 | 132 | |
|
134 | 133 | for name, obj in mapping.items(): |
|
135 | 134 | mapping[name] = can(obj) |
|
136 | 135 | return depend(_require, *names, **mapping) |
|
137 | 136 | |
|
138 | 137 | class Dependency(set): |
|
139 | 138 | """An object for representing a set of msg_id dependencies. |
|
140 | 139 | |
|
141 | 140 | Subclassed from set(). |
|
142 | 141 | |
|
143 | 142 | Parameters |
|
144 | 143 | ---------- |
|
145 | 144 | dependencies: list/set of msg_ids or AsyncResult objects or output of Dependency.as_dict() |
|
146 | 145 | The msg_ids to depend on |
|
147 | 146 | all : bool [default True] |
|
148 | 147 | Whether the dependency should be considered met when *all* depending tasks have completed |
|
149 | 148 | or only when *any* have been completed. |
|
150 | 149 | success : bool [default True] |
|
151 | 150 | Whether to consider successes as fulfilling dependencies. |
|
152 | 151 | failure : bool [default False] |
|
153 | 152 | Whether to consider failures as fulfilling dependencies. |
|
154 | 153 | |
|
155 | 154 | If `all=success=True` and `failure=False`, then the task will fail with an ImpossibleDependency |
|
156 | 155 | as soon as the first depended-upon task fails. |
|
157 | 156 | """ |
|
158 | 157 | |
|
159 | 158 | all=True |
|
160 | 159 | success=True |
|
161 | 160 | failure=True |
|
162 | 161 | |
|
163 | 162 | def __init__(self, dependencies=[], all=True, success=True, failure=False): |
|
164 | 163 | if isinstance(dependencies, dict): |
|
165 | 164 | # load from dict |
|
166 | 165 | all = dependencies.get('all', True) |
|
167 | 166 | success = dependencies.get('success', success) |
|
168 | 167 | failure = dependencies.get('failure', failure) |
|
169 | 168 | dependencies = dependencies.get('dependencies', []) |
|
170 | 169 | ids = [] |
|
171 | 170 | |
|
172 | 171 | # extract ids from various sources: |
|
173 | 172 | if isinstance(dependencies, string_types + (AsyncResult,)): |
|
174 | 173 | dependencies = [dependencies] |
|
175 | 174 | for d in dependencies: |
|
176 | 175 | if isinstance(d, string_types): |
|
177 | 176 | ids.append(d) |
|
178 | 177 | elif isinstance(d, AsyncResult): |
|
179 | 178 | ids.extend(d.msg_ids) |
|
180 | 179 | else: |
|
181 | 180 | raise TypeError("invalid dependency type: %r"%type(d)) |
|
182 | 181 | |
|
183 | 182 | set.__init__(self, ids) |
|
184 | 183 | self.all = all |
|
185 | 184 | if not (success or failure): |
|
186 | 185 | raise ValueError("Must depend on at least one of successes or failures!") |
|
187 | 186 | self.success=success |
|
188 | 187 | self.failure = failure |
|
189 | 188 | |
|
190 | 189 | def check(self, completed, failed=None): |
|
191 | 190 | """check whether our dependencies have been met.""" |
|
192 | 191 | if len(self) == 0: |
|
193 | 192 | return True |
|
194 | 193 | against = set() |
|
195 | 194 | if self.success: |
|
196 | 195 | against = completed |
|
197 | 196 | if failed is not None and self.failure: |
|
198 | 197 | against = against.union(failed) |
|
199 | 198 | if self.all: |
|
200 | 199 | return self.issubset(against) |
|
201 | 200 | else: |
|
202 | 201 | return not self.isdisjoint(against) |
|
203 | 202 | |
|
204 | 203 | def unreachable(self, completed, failed=None): |
|
205 | 204 | """return whether this dependency has become impossible.""" |
|
206 | 205 | if len(self) == 0: |
|
207 | 206 | return False |
|
208 | 207 | against = set() |
|
209 | 208 | if not self.success: |
|
210 | 209 | against = completed |
|
211 | 210 | if failed is not None and not self.failure: |
|
212 | 211 | against = against.union(failed) |
|
213 | 212 | if self.all: |
|
214 | 213 | return not self.isdisjoint(against) |
|
215 | 214 | else: |
|
216 | 215 | return self.issubset(against) |
|
217 | 216 | |
|
218 | 217 | |
|
219 | 218 | def as_dict(self): |
|
220 | 219 | """Represent this dependency as a dict. For json compatibility.""" |
|
221 | 220 | return dict( |
|
222 | 221 | dependencies=list(self), |
|
223 | 222 | all=self.all, |
|
224 | 223 | success=self.success, |
|
225 | 224 | failure=self.failure |
|
226 | 225 | ) |
|
227 | 226 | |
|
228 | 227 | |
|
229 | 228 | __all__ = ['depend', 'require', 'dependent', 'Dependency'] |
|
230 | 229 |
@@ -1,312 +1,317 b'' | |||
|
1 | 1 | """A Task logger that presents our DB interface, |
|
2 | 2 | but exists entirely in memory and implemented with dicts. |
|
3 | 3 | |
|
4 | 4 | Authors: |
|
5 | 5 | |
|
6 | 6 | * Min RK |
|
7 | 7 | |
|
8 | 8 | |
|
9 | TaskRecords are dicts of the form: | |
|
10 | { | |
|
11 | 'msg_id' : str(uuid), | |
|
12 |
|
|
|
13 |
|
|
|
14 | 'header' : dict(header), | |
|
15 | 'content': dict(content), | |
|
16 | 'buffers': list(buffers), | |
|
17 | 'submitted': datetime, | |
|
18 |
'st |
|
|
19 |
|
|
|
20 |
|
|
|
21 |
'resu |
|
|
22 |
'result_ |
|
|
23 |
'result_ |
|
|
24 | } | |
|
25 | With this info, many of the special categories of tasks can be defined by query: | |
|
26 | ||
|
27 | pending: completed is None | |
|
28 | client's outstanding: client_uuid = uuid && completed is None | |
|
29 | MIA: arrived is None (and completed is None) | |
|
30 | etc. | |
|
9 | TaskRecords are dicts of the form:: | |
|
10 | ||
|
11 | { | |
|
12 | 'msg_id' : str(uuid), | |
|
13 | 'client_uuid' : str(uuid), | |
|
14 | 'engine_uuid' : str(uuid) or None, | |
|
15 | 'header' : dict(header), | |
|
16 | 'content': dict(content), | |
|
17 | 'buffers': list(buffers), | |
|
18 | 'submitted': datetime, | |
|
19 | 'started': datetime or None, | |
|
20 | 'completed': datetime or None, | |
|
21 | 'resubmitted': datetime or None, | |
|
22 | 'result_header' : dict(header) or None, | |
|
23 | 'result_content' : dict(content) or None, | |
|
24 | 'result_buffers' : list(buffers) or None, | |
|
25 | } | |
|
26 | ||
|
27 | With this info, many of the special categories of tasks can be defined by query, | |
|
28 | e.g.: | |
|
29 | ||
|
30 | * pending: completed is None | |
|
31 | * client's outstanding: client_uuid = uuid && completed is None | |
|
32 | * MIA: arrived is None (and completed is None) | |
|
33 | ||
|
34 | EngineRecords are dicts of the form:: | |
|
35 | ||
|
36 | { | |
|
37 | 'eid' : int(id), | |
|
38 | 'uuid': str(uuid) | |
|
39 | } | |
|
31 | 40 | |
|
32 | EngineRecords are dicts of the form: | |
|
33 | { | |
|
34 | 'eid' : int(id), | |
|
35 | 'uuid': str(uuid) | |
|
36 | } | |
|
37 | 41 | This may be extended, but is currently. |
|
38 | 42 | |
|
39 | We support a subset of mongodb operators: | |
|
43 | We support a subset of mongodb operators:: | |
|
44 | ||
|
40 | 45 | $lt,$gt,$lte,$gte,$ne,$in,$nin,$all,$mod,$exists |
|
41 | 46 | """ |
|
42 | 47 | #----------------------------------------------------------------------------- |
|
43 | 48 | # Copyright (C) 2010-2011 The IPython Development Team |
|
44 | 49 | # |
|
45 | 50 | # Distributed under the terms of the BSD License. The full license is in |
|
46 | 51 | # the file COPYING, distributed as part of this software. |
|
47 | 52 | #----------------------------------------------------------------------------- |
|
48 | 53 | |
|
49 | 54 | from copy import deepcopy as copy |
|
50 | 55 | from datetime import datetime |
|
51 | 56 | |
|
52 | 57 | from IPython.config.configurable import LoggingConfigurable |
|
53 | 58 | |
|
54 | 59 | from IPython.utils.py3compat import iteritems, itervalues |
|
55 | 60 | from IPython.utils.traitlets import Dict, Unicode, Integer, Float |
|
56 | 61 | |
|
57 | 62 | filters = { |
|
58 | 63 | '$lt' : lambda a,b: a < b, |
|
59 | 64 | '$gt' : lambda a,b: b > a, |
|
60 | 65 | '$eq' : lambda a,b: a == b, |
|
61 | 66 | '$ne' : lambda a,b: a != b, |
|
62 | 67 | '$lte': lambda a,b: a <= b, |
|
63 | 68 | '$gte': lambda a,b: a >= b, |
|
64 | 69 | '$in' : lambda a,b: a in b, |
|
65 | 70 | '$nin': lambda a,b: a not in b, |
|
66 | 71 | '$all': lambda a,b: all([ a in bb for bb in b ]), |
|
67 | 72 | '$mod': lambda a,b: a%b[0] == b[1], |
|
68 | 73 | '$exists' : lambda a,b: (b and a is not None) or (a is None and not b) |
|
69 | 74 | } |
|
70 | 75 | |
|
71 | 76 | |
|
72 | 77 | class CompositeFilter(object): |
|
73 | 78 | """Composite filter for matching multiple properties.""" |
|
74 | 79 | |
|
75 | 80 | def __init__(self, dikt): |
|
76 | 81 | self.tests = [] |
|
77 | 82 | self.values = [] |
|
78 | 83 | for key, value in iteritems(dikt): |
|
79 | 84 | self.tests.append(filters[key]) |
|
80 | 85 | self.values.append(value) |
|
81 | 86 | |
|
82 | 87 | def __call__(self, value): |
|
83 | 88 | for test,check in zip(self.tests, self.values): |
|
84 | 89 | if not test(value, check): |
|
85 | 90 | return False |
|
86 | 91 | return True |
|
87 | 92 | |
|
88 | 93 | class BaseDB(LoggingConfigurable): |
|
89 | 94 | """Empty Parent class so traitlets work on DB.""" |
|
90 | 95 | # base configurable traits: |
|
91 | 96 | session = Unicode("") |
|
92 | 97 | |
|
93 | 98 | class DictDB(BaseDB): |
|
94 | 99 | """Basic in-memory dict-based object for saving Task Records. |
|
95 | 100 | |
|
96 | 101 | This is the first object to present the DB interface |
|
97 | 102 | for logging tasks out of memory. |
|
98 | 103 | |
|
99 | 104 | The interface is based on MongoDB, so adding a MongoDB |
|
100 | 105 | backend should be straightforward. |
|
101 | 106 | """ |
|
102 | 107 | |
|
103 | 108 | _records = Dict() |
|
104 | 109 | _culled_ids = set() # set of ids which have been culled |
|
105 | 110 | _buffer_bytes = Integer(0) # running total of the bytes in the DB |
|
106 | 111 | |
|
107 | 112 | size_limit = Integer(1024**3, config=True, |
|
108 | 113 | help="""The maximum total size (in bytes) of the buffers stored in the db |
|
109 | 114 | |
|
110 | 115 | When the db exceeds this size, the oldest records will be culled until |
|
111 | 116 | the total size is under size_limit * (1-cull_fraction). |
|
112 | 117 | default: 1 GB |
|
113 | 118 | """ |
|
114 | 119 | ) |
|
115 | 120 | record_limit = Integer(1024, config=True, |
|
116 | 121 | help="""The maximum number of records in the db |
|
117 | 122 | |
|
118 | 123 | When the history exceeds this size, the first record_limit * cull_fraction |
|
119 | 124 | records will be culled. |
|
120 | 125 | """ |
|
121 | 126 | ) |
|
122 | 127 | cull_fraction = Float(0.1, config=True, |
|
123 | 128 | help="""The fraction by which the db should culled when one of the limits is exceeded |
|
124 | 129 | |
|
125 | 130 | In general, the db size will spend most of its time with a size in the range: |
|
126 | 131 | |
|
127 | 132 | [limit * (1-cull_fraction), limit] |
|
128 | 133 | |
|
129 | 134 | for each of size_limit and record_limit. |
|
130 | 135 | """ |
|
131 | 136 | ) |
|
132 | 137 | |
|
133 | 138 | def _match_one(self, rec, tests): |
|
134 | 139 | """Check if a specific record matches tests.""" |
|
135 | 140 | for key,test in iteritems(tests): |
|
136 | 141 | if not test(rec.get(key, None)): |
|
137 | 142 | return False |
|
138 | 143 | return True |
|
139 | 144 | |
|
140 | 145 | def _match(self, check): |
|
141 | 146 | """Find all the matches for a check dict.""" |
|
142 | 147 | matches = [] |
|
143 | 148 | tests = {} |
|
144 | 149 | for k,v in iteritems(check): |
|
145 | 150 | if isinstance(v, dict): |
|
146 | 151 | tests[k] = CompositeFilter(v) |
|
147 | 152 | else: |
|
148 | 153 | tests[k] = lambda o: o==v |
|
149 | 154 | |
|
150 | 155 | for rec in itervalues(self._records): |
|
151 | 156 | if self._match_one(rec, tests): |
|
152 | 157 | matches.append(copy(rec)) |
|
153 | 158 | return matches |
|
154 | 159 | |
|
155 | 160 | def _extract_subdict(self, rec, keys): |
|
156 | 161 | """extract subdict of keys""" |
|
157 | 162 | d = {} |
|
158 | 163 | d['msg_id'] = rec['msg_id'] |
|
159 | 164 | for key in keys: |
|
160 | 165 | d[key] = rec[key] |
|
161 | 166 | return copy(d) |
|
162 | 167 | |
|
163 | 168 | # methods for monitoring size / culling history |
|
164 | 169 | |
|
165 | 170 | def _add_bytes(self, rec): |
|
166 | 171 | for key in ('buffers', 'result_buffers'): |
|
167 | 172 | for buf in rec.get(key) or []: |
|
168 | 173 | self._buffer_bytes += len(buf) |
|
169 | 174 | |
|
170 | 175 | self._maybe_cull() |
|
171 | 176 | |
|
172 | 177 | def _drop_bytes(self, rec): |
|
173 | 178 | for key in ('buffers', 'result_buffers'): |
|
174 | 179 | for buf in rec.get(key) or []: |
|
175 | 180 | self._buffer_bytes -= len(buf) |
|
176 | 181 | |
|
177 | 182 | def _cull_oldest(self, n=1): |
|
178 | 183 | """cull the oldest N records""" |
|
179 | 184 | for msg_id in self.get_history()[:n]: |
|
180 | 185 | self.log.debug("Culling record: %r", msg_id) |
|
181 | 186 | self._culled_ids.add(msg_id) |
|
182 | 187 | self.drop_record(msg_id) |
|
183 | 188 | |
|
184 | 189 | def _maybe_cull(self): |
|
185 | 190 | # cull by count: |
|
186 | 191 | if len(self._records) > self.record_limit: |
|
187 | 192 | to_cull = int(self.cull_fraction * self.record_limit) |
|
188 | 193 | self.log.info("%i records exceeds limit of %i, culling oldest %i", |
|
189 | 194 | len(self._records), self.record_limit, to_cull |
|
190 | 195 | ) |
|
191 | 196 | self._cull_oldest(to_cull) |
|
192 | 197 | |
|
193 | 198 | # cull by size: |
|
194 | 199 | if self._buffer_bytes > self.size_limit: |
|
195 | 200 | limit = self.size_limit * (1 - self.cull_fraction) |
|
196 | 201 | |
|
197 | 202 | before = self._buffer_bytes |
|
198 | 203 | before_count = len(self._records) |
|
199 | 204 | culled = 0 |
|
200 | 205 | while self._buffer_bytes > limit: |
|
201 | 206 | self._cull_oldest(1) |
|
202 | 207 | culled += 1 |
|
203 | 208 | |
|
204 | 209 | self.log.info("%i records with total buffer size %i exceeds limit: %i. Culled oldest %i records.", |
|
205 | 210 | before_count, before, self.size_limit, culled |
|
206 | 211 | ) |
|
207 | 212 | |
|
208 | 213 | # public API methods: |
|
209 | 214 | |
|
210 | 215 | def add_record(self, msg_id, rec): |
|
211 | 216 | """Add a new Task Record, by msg_id.""" |
|
212 | 217 | if msg_id in self._records: |
|
213 | 218 | raise KeyError("Already have msg_id %r"%(msg_id)) |
|
214 | 219 | self._records[msg_id] = rec |
|
215 | 220 | self._add_bytes(rec) |
|
216 | 221 | self._maybe_cull() |
|
217 | 222 | |
|
218 | 223 | def get_record(self, msg_id): |
|
219 | 224 | """Get a specific Task Record, by msg_id.""" |
|
220 | 225 | if msg_id in self._culled_ids: |
|
221 | 226 | raise KeyError("Record %r has been culled for size" % msg_id) |
|
222 | 227 | if not msg_id in self._records: |
|
223 | 228 | raise KeyError("No such msg_id %r"%(msg_id)) |
|
224 | 229 | return copy(self._records[msg_id]) |
|
225 | 230 | |
|
226 | 231 | def update_record(self, msg_id, rec): |
|
227 | 232 | """Update the data in an existing record.""" |
|
228 | 233 | if msg_id in self._culled_ids: |
|
229 | 234 | raise KeyError("Record %r has been culled for size" % msg_id) |
|
230 | 235 | _rec = self._records[msg_id] |
|
231 | 236 | self._drop_bytes(_rec) |
|
232 | 237 | _rec.update(rec) |
|
233 | 238 | self._add_bytes(_rec) |
|
234 | 239 | |
|
235 | 240 | def drop_matching_records(self, check): |
|
236 | 241 | """Remove a record from the DB.""" |
|
237 | 242 | matches = self._match(check) |
|
238 | 243 | for rec in matches: |
|
239 | 244 | self._drop_bytes(rec) |
|
240 | 245 | del self._records[rec['msg_id']] |
|
241 | 246 | |
|
242 | 247 | def drop_record(self, msg_id): |
|
243 | 248 | """Remove a record from the DB.""" |
|
244 | 249 | rec = self._records[msg_id] |
|
245 | 250 | self._drop_bytes(rec) |
|
246 | 251 | del self._records[msg_id] |
|
247 | 252 | |
|
248 | 253 | def find_records(self, check, keys=None): |
|
249 | 254 | """Find records matching a query dict, optionally extracting subset of keys. |
|
250 | 255 | |
|
251 | 256 | Returns dict keyed by msg_id of matching records. |
|
252 | 257 | |
|
253 | 258 | Parameters |
|
254 | 259 | ---------- |
|
255 | 260 | |
|
256 | 261 | check: dict |
|
257 | 262 | mongodb-style query argument |
|
258 | 263 | keys: list of strs [optional] |
|
259 | 264 | if specified, the subset of keys to extract. msg_id will *always* be |
|
260 | 265 | included. |
|
261 | 266 | """ |
|
262 | 267 | matches = self._match(check) |
|
263 | 268 | if keys: |
|
264 | 269 | return [ self._extract_subdict(rec, keys) for rec in matches ] |
|
265 | 270 | else: |
|
266 | 271 | return matches |
|
267 | 272 | |
|
268 | 273 | def get_history(self): |
|
269 | 274 | """get all msg_ids, ordered by time submitted.""" |
|
270 | 275 | msg_ids = self._records.keys() |
|
271 | 276 | # Remove any that do not have a submitted timestamp. |
|
272 | 277 | # This is extremely unlikely to happen, |
|
273 | 278 | # but it seems to come up in some tests on VMs. |
|
274 | 279 | msg_ids = [ m for m in msg_ids if self._records[m]['submitted'] is not None ] |
|
275 | 280 | return sorted(msg_ids, key=lambda m: self._records[m]['submitted']) |
|
276 | 281 | |
|
277 | 282 | |
|
278 | 283 | NODATA = KeyError("NoDB backend doesn't store any data. " |
|
279 | 284 | "Start the Controller with a DB backend to enable resubmission / result persistence." |
|
280 | 285 | ) |
|
281 | 286 | |
|
282 | 287 | |
|
283 | 288 | class NoDB(BaseDB): |
|
284 | 289 | """A blackhole db backend that actually stores no information. |
|
285 | 290 | |
|
286 | 291 | Provides the full DB interface, but raises KeyErrors on any |
|
287 | 292 | method that tries to access the records. This can be used to |
|
288 | 293 | minimize the memory footprint of the Hub when its record-keeping |
|
289 | 294 | functionality is not required. |
|
290 | 295 | """ |
|
291 | 296 | |
|
292 | 297 | def add_record(self, msg_id, record): |
|
293 | 298 | pass |
|
294 | 299 | |
|
295 | 300 | def get_record(self, msg_id): |
|
296 | 301 | raise NODATA |
|
297 | 302 | |
|
298 | 303 | def update_record(self, msg_id, record): |
|
299 | 304 | pass |
|
300 | 305 | |
|
301 | 306 | def drop_matching_records(self, check): |
|
302 | 307 | pass |
|
303 | 308 | |
|
304 | 309 | def drop_record(self, msg_id): |
|
305 | 310 | pass |
|
306 | 311 | |
|
307 | 312 | def find_records(self, check, keys=None): |
|
308 | 313 | raise NODATA |
|
309 | 314 | |
|
310 | 315 | def get_history(self): |
|
311 | 316 | raise NODATA |
|
312 | 317 |
@@ -1,1422 +1,1426 b'' | |||
|
1 | 1 | """The IPython Controller Hub with 0MQ |
|
2 | 2 | This is the master object that handles connections from engines and clients, |
|
3 | 3 | and monitors traffic through the various queues. |
|
4 | 4 | |
|
5 | 5 | Authors: |
|
6 | 6 | |
|
7 | 7 | * Min RK |
|
8 | 8 | """ |
|
9 | 9 | #----------------------------------------------------------------------------- |
|
10 | 10 | # Copyright (C) 2010-2011 The IPython Development Team |
|
11 | 11 | # |
|
12 | 12 | # Distributed under the terms of the BSD License. The full license is in |
|
13 | 13 | # the file COPYING, distributed as part of this software. |
|
14 | 14 | #----------------------------------------------------------------------------- |
|
15 | 15 | |
|
16 | 16 | #----------------------------------------------------------------------------- |
|
17 | 17 | # Imports |
|
18 | 18 | #----------------------------------------------------------------------------- |
|
19 | 19 | from __future__ import print_function |
|
20 | 20 | |
|
21 | 21 | import json |
|
22 | 22 | import os |
|
23 | 23 | import sys |
|
24 | 24 | import time |
|
25 | 25 | from datetime import datetime |
|
26 | 26 | |
|
27 | 27 | import zmq |
|
28 | 28 | from zmq.eventloop import ioloop |
|
29 | 29 | from zmq.eventloop.zmqstream import ZMQStream |
|
30 | 30 | |
|
31 | 31 | # internal: |
|
32 | 32 | from IPython.utils.importstring import import_item |
|
33 | 33 | from IPython.utils.jsonutil import extract_dates |
|
34 | 34 | from IPython.utils.localinterfaces import localhost |
|
35 | 35 | from IPython.utils.py3compat import cast_bytes, unicode_type, iteritems |
|
36 | 36 | from IPython.utils.traitlets import ( |
|
37 | 37 | HasTraits, Instance, Integer, Unicode, Dict, Set, Tuple, CBytes, DottedObjectName |
|
38 | 38 | ) |
|
39 | 39 | |
|
40 | 40 | from IPython.parallel import error, util |
|
41 | 41 | from IPython.parallel.factory import RegistrationFactory |
|
42 | 42 | |
|
43 | 43 | from IPython.kernel.zmq.session import SessionFactory |
|
44 | 44 | |
|
45 | 45 | from .heartmonitor import HeartMonitor |
|
46 | 46 | |
|
47 | 47 | #----------------------------------------------------------------------------- |
|
48 | 48 | # Code |
|
49 | 49 | #----------------------------------------------------------------------------- |
|
50 | 50 | |
|
51 | 51 | def _passer(*args, **kwargs): |
|
52 | 52 | return |
|
53 | 53 | |
|
54 | 54 | def _printer(*args, **kwargs): |
|
55 | 55 | print (args) |
|
56 | 56 | print (kwargs) |
|
57 | 57 | |
|
58 | 58 | def empty_record(): |
|
59 | 59 | """Return an empty dict with all record keys.""" |
|
60 | 60 | return { |
|
61 | 61 | 'msg_id' : None, |
|
62 | 62 | 'header' : None, |
|
63 | 63 | 'metadata' : None, |
|
64 | 64 | 'content': None, |
|
65 | 65 | 'buffers': None, |
|
66 | 66 | 'submitted': None, |
|
67 | 67 | 'client_uuid' : None, |
|
68 | 68 | 'engine_uuid' : None, |
|
69 | 69 | 'started': None, |
|
70 | 70 | 'completed': None, |
|
71 | 71 | 'resubmitted': None, |
|
72 | 72 | 'received': None, |
|
73 | 73 | 'result_header' : None, |
|
74 | 74 | 'result_metadata' : None, |
|
75 | 75 | 'result_content' : None, |
|
76 | 76 | 'result_buffers' : None, |
|
77 | 77 | 'queue' : None, |
|
78 | 78 | 'pyin' : None, |
|
79 | 79 | 'pyout': None, |
|
80 | 80 | 'pyerr': None, |
|
81 | 81 | 'stdout': '', |
|
82 | 82 | 'stderr': '', |
|
83 | 83 | } |
|
84 | 84 | |
|
85 | 85 | def init_record(msg): |
|
86 | 86 | """Initialize a TaskRecord based on a request.""" |
|
87 | 87 | header = msg['header'] |
|
88 | 88 | return { |
|
89 | 89 | 'msg_id' : header['msg_id'], |
|
90 | 90 | 'header' : header, |
|
91 | 91 | 'content': msg['content'], |
|
92 | 92 | 'metadata': msg['metadata'], |
|
93 | 93 | 'buffers': msg['buffers'], |
|
94 | 94 | 'submitted': header['date'], |
|
95 | 95 | 'client_uuid' : None, |
|
96 | 96 | 'engine_uuid' : None, |
|
97 | 97 | 'started': None, |
|
98 | 98 | 'completed': None, |
|
99 | 99 | 'resubmitted': None, |
|
100 | 100 | 'received': None, |
|
101 | 101 | 'result_header' : None, |
|
102 | 102 | 'result_metadata': None, |
|
103 | 103 | 'result_content' : None, |
|
104 | 104 | 'result_buffers' : None, |
|
105 | 105 | 'queue' : None, |
|
106 | 106 | 'pyin' : None, |
|
107 | 107 | 'pyout': None, |
|
108 | 108 | 'pyerr': None, |
|
109 | 109 | 'stdout': '', |
|
110 | 110 | 'stderr': '', |
|
111 | 111 | } |
|
112 | 112 | |
|
113 | 113 | |
|
114 | 114 | class EngineConnector(HasTraits): |
|
115 | 115 | """A simple object for accessing the various zmq connections of an object. |
|
116 | 116 | Attributes are: |
|
117 | 117 | id (int): engine ID |
|
118 | 118 | uuid (unicode): engine UUID |
|
119 | 119 | pending: set of msg_ids |
|
120 | 120 | stallback: DelayedCallback for stalled registration |
|
121 | 121 | """ |
|
122 | 122 | |
|
123 | 123 | id = Integer(0) |
|
124 | 124 | uuid = Unicode() |
|
125 | 125 | pending = Set() |
|
126 | 126 | stallback = Instance(ioloop.DelayedCallback) |
|
127 | 127 | |
|
128 | 128 | |
|
129 | 129 | _db_shortcuts = { |
|
130 | 130 | 'sqlitedb' : 'IPython.parallel.controller.sqlitedb.SQLiteDB', |
|
131 | 131 | 'mongodb' : 'IPython.parallel.controller.mongodb.MongoDB', |
|
132 | 132 | 'dictdb' : 'IPython.parallel.controller.dictdb.DictDB', |
|
133 | 133 | 'nodb' : 'IPython.parallel.controller.dictdb.NoDB', |
|
134 | 134 | } |
|
135 | 135 | |
|
136 | 136 | class HubFactory(RegistrationFactory): |
|
137 | 137 | """The Configurable for setting up a Hub.""" |
|
138 | 138 | |
|
139 | 139 | # port-pairs for monitoredqueues: |
|
140 | 140 | hb = Tuple(Integer,Integer,config=True, |
|
141 | 141 | help="""PUB/ROUTER Port pair for Engine heartbeats""") |
|
142 | 142 | def _hb_default(self): |
|
143 | 143 | return tuple(util.select_random_ports(2)) |
|
144 | 144 | |
|
145 | 145 | mux = Tuple(Integer,Integer,config=True, |
|
146 | 146 | help="""Client/Engine Port pair for MUX queue""") |
|
147 | 147 | |
|
148 | 148 | def _mux_default(self): |
|
149 | 149 | return tuple(util.select_random_ports(2)) |
|
150 | 150 | |
|
151 | 151 | task = Tuple(Integer,Integer,config=True, |
|
152 | 152 | help="""Client/Engine Port pair for Task queue""") |
|
153 | 153 | def _task_default(self): |
|
154 | 154 | return tuple(util.select_random_ports(2)) |
|
155 | 155 | |
|
156 | 156 | control = Tuple(Integer,Integer,config=True, |
|
157 | 157 | help="""Client/Engine Port pair for Control queue""") |
|
158 | 158 | |
|
159 | 159 | def _control_default(self): |
|
160 | 160 | return tuple(util.select_random_ports(2)) |
|
161 | 161 | |
|
162 | 162 | iopub = Tuple(Integer,Integer,config=True, |
|
163 | 163 | help="""Client/Engine Port pair for IOPub relay""") |
|
164 | 164 | |
|
165 | 165 | def _iopub_default(self): |
|
166 | 166 | return tuple(util.select_random_ports(2)) |
|
167 | 167 | |
|
168 | 168 | # single ports: |
|
169 | 169 | mon_port = Integer(config=True, |
|
170 | 170 | help="""Monitor (SUB) port for queue traffic""") |
|
171 | 171 | |
|
172 | 172 | def _mon_port_default(self): |
|
173 | 173 | return util.select_random_ports(1)[0] |
|
174 | 174 | |
|
175 | 175 | notifier_port = Integer(config=True, |
|
176 | 176 | help="""PUB port for sending engine status notifications""") |
|
177 | 177 | |
|
178 | 178 | def _notifier_port_default(self): |
|
179 | 179 | return util.select_random_ports(1)[0] |
|
180 | 180 | |
|
181 | 181 | engine_ip = Unicode(config=True, |
|
182 | 182 | help="IP on which to listen for engine connections. [default: loopback]") |
|
183 | 183 | def _engine_ip_default(self): |
|
184 | 184 | return localhost() |
|
185 | 185 | engine_transport = Unicode('tcp', config=True, |
|
186 | 186 | help="0MQ transport for engine connections. [default: tcp]") |
|
187 | 187 | |
|
188 | 188 | client_ip = Unicode(config=True, |
|
189 | 189 | help="IP on which to listen for client connections. [default: loopback]") |
|
190 | 190 | client_transport = Unicode('tcp', config=True, |
|
191 | 191 | help="0MQ transport for client connections. [default : tcp]") |
|
192 | 192 | |
|
193 | 193 | monitor_ip = Unicode(config=True, |
|
194 | 194 | help="IP on which to listen for monitor messages. [default: loopback]") |
|
195 | 195 | monitor_transport = Unicode('tcp', config=True, |
|
196 | 196 | help="0MQ transport for monitor messages. [default : tcp]") |
|
197 | 197 | |
|
198 | 198 | _client_ip_default = _monitor_ip_default = _engine_ip_default |
|
199 | 199 | |
|
200 | 200 | |
|
201 | 201 | monitor_url = Unicode('') |
|
202 | 202 | |
|
203 | 203 | db_class = DottedObjectName('NoDB', |
|
204 | 204 | config=True, help="""The class to use for the DB backend |
|
205 | 205 | |
|
206 | 206 | Options include: |
|
207 | 207 | |
|
208 | 208 | SQLiteDB: SQLite |
|
209 | 209 | MongoDB : use MongoDB |
|
210 | 210 | DictDB : in-memory storage (fastest, but be mindful of memory growth of the Hub) |
|
211 | 211 | NoDB : disable database altogether (default) |
|
212 | 212 | |
|
213 | 213 | """) |
|
214 | 214 | |
|
215 | 215 | # not configurable |
|
216 | 216 | db = Instance('IPython.parallel.controller.dictdb.BaseDB') |
|
217 | 217 | heartmonitor = Instance('IPython.parallel.controller.heartmonitor.HeartMonitor') |
|
218 | 218 | |
|
219 | 219 | def _ip_changed(self, name, old, new): |
|
220 | 220 | self.engine_ip = new |
|
221 | 221 | self.client_ip = new |
|
222 | 222 | self.monitor_ip = new |
|
223 | 223 | self._update_monitor_url() |
|
224 | 224 | |
|
225 | 225 | def _update_monitor_url(self): |
|
226 | 226 | self.monitor_url = "%s://%s:%i" % (self.monitor_transport, self.monitor_ip, self.mon_port) |
|
227 | 227 | |
|
228 | 228 | def _transport_changed(self, name, old, new): |
|
229 | 229 | self.engine_transport = new |
|
230 | 230 | self.client_transport = new |
|
231 | 231 | self.monitor_transport = new |
|
232 | 232 | self._update_monitor_url() |
|
233 | 233 | |
|
234 | 234 | def __init__(self, **kwargs): |
|
235 | 235 | super(HubFactory, self).__init__(**kwargs) |
|
236 | 236 | self._update_monitor_url() |
|
237 | 237 | |
|
238 | 238 | |
|
239 | 239 | def construct(self): |
|
240 | 240 | self.init_hub() |
|
241 | 241 | |
|
242 | 242 | def start(self): |
|
243 | 243 | self.heartmonitor.start() |
|
244 | 244 | self.log.info("Heartmonitor started") |
|
245 | 245 | |
|
246 | 246 | def client_url(self, channel): |
|
247 | 247 | """return full zmq url for a named client channel""" |
|
248 | 248 | return "%s://%s:%i" % (self.client_transport, self.client_ip, self.client_info[channel]) |
|
249 | 249 | |
|
250 | 250 | def engine_url(self, channel): |
|
251 | 251 | """return full zmq url for a named engine channel""" |
|
252 | 252 | return "%s://%s:%i" % (self.engine_transport, self.engine_ip, self.engine_info[channel]) |
|
253 | 253 | |
|
254 | 254 | def init_hub(self): |
|
255 | 255 | """construct Hub object""" |
|
256 | 256 | |
|
257 | 257 | ctx = self.context |
|
258 | 258 | loop = self.loop |
|
259 | 259 | if 'TaskScheduler.scheme_name' in self.config: |
|
260 | 260 | scheme = self.config.TaskScheduler.scheme_name |
|
261 | 261 | else: |
|
262 | 262 | from .scheduler import TaskScheduler |
|
263 | 263 | scheme = TaskScheduler.scheme_name.get_default_value() |
|
264 | 264 | |
|
265 | 265 | # build connection dicts |
|
266 | 266 | engine = self.engine_info = { |
|
267 | 267 | 'interface' : "%s://%s" % (self.engine_transport, self.engine_ip), |
|
268 | 268 | 'registration' : self.regport, |
|
269 | 269 | 'control' : self.control[1], |
|
270 | 270 | 'mux' : self.mux[1], |
|
271 | 271 | 'hb_ping' : self.hb[0], |
|
272 | 272 | 'hb_pong' : self.hb[1], |
|
273 | 273 | 'task' : self.task[1], |
|
274 | 274 | 'iopub' : self.iopub[1], |
|
275 | 275 | } |
|
276 | 276 | |
|
277 | 277 | client = self.client_info = { |
|
278 | 278 | 'interface' : "%s://%s" % (self.client_transport, self.client_ip), |
|
279 | 279 | 'registration' : self.regport, |
|
280 | 280 | 'control' : self.control[0], |
|
281 | 281 | 'mux' : self.mux[0], |
|
282 | 282 | 'task' : self.task[0], |
|
283 | 283 | 'task_scheme' : scheme, |
|
284 | 284 | 'iopub' : self.iopub[0], |
|
285 | 285 | 'notification' : self.notifier_port, |
|
286 | 286 | } |
|
287 | 287 | |
|
288 | 288 | self.log.debug("Hub engine addrs: %s", self.engine_info) |
|
289 | 289 | self.log.debug("Hub client addrs: %s", self.client_info) |
|
290 | 290 | |
|
291 | 291 | # Registrar socket |
|
292 | 292 | q = ZMQStream(ctx.socket(zmq.ROUTER), loop) |
|
293 | 293 | util.set_hwm(q, 0) |
|
294 | 294 | q.bind(self.client_url('registration')) |
|
295 | 295 | self.log.info("Hub listening on %s for registration.", self.client_url('registration')) |
|
296 | 296 | if self.client_ip != self.engine_ip: |
|
297 | 297 | q.bind(self.engine_url('registration')) |
|
298 | 298 | self.log.info("Hub listening on %s for registration.", self.engine_url('registration')) |
|
299 | 299 | |
|
300 | 300 | ### Engine connections ### |
|
301 | 301 | |
|
302 | 302 | # heartbeat |
|
303 | 303 | hpub = ctx.socket(zmq.PUB) |
|
304 | 304 | hpub.bind(self.engine_url('hb_ping')) |
|
305 | 305 | hrep = ctx.socket(zmq.ROUTER) |
|
306 | 306 | util.set_hwm(hrep, 0) |
|
307 | 307 | hrep.bind(self.engine_url('hb_pong')) |
|
308 | 308 | self.heartmonitor = HeartMonitor(loop=loop, parent=self, log=self.log, |
|
309 | 309 | pingstream=ZMQStream(hpub,loop), |
|
310 | 310 | pongstream=ZMQStream(hrep,loop) |
|
311 | 311 | ) |
|
312 | 312 | |
|
313 | 313 | ### Client connections ### |
|
314 | 314 | |
|
315 | 315 | # Notifier socket |
|
316 | 316 | n = ZMQStream(ctx.socket(zmq.PUB), loop) |
|
317 | 317 | n.bind(self.client_url('notification')) |
|
318 | 318 | |
|
319 | 319 | ### build and launch the queues ### |
|
320 | 320 | |
|
321 | 321 | # monitor socket |
|
322 | 322 | sub = ctx.socket(zmq.SUB) |
|
323 | 323 | sub.setsockopt(zmq.SUBSCRIBE, b"") |
|
324 | 324 | sub.bind(self.monitor_url) |
|
325 | 325 | sub.bind('inproc://monitor') |
|
326 | 326 | sub = ZMQStream(sub, loop) |
|
327 | 327 | |
|
328 | 328 | # connect the db |
|
329 | 329 | db_class = _db_shortcuts.get(self.db_class.lower(), self.db_class) |
|
330 | 330 | self.log.info('Hub using DB backend: %r', (db_class.split('.')[-1])) |
|
331 | 331 | self.db = import_item(str(db_class))(session=self.session.session, |
|
332 | 332 | parent=self, log=self.log) |
|
333 | 333 | time.sleep(.25) |
|
334 | 334 | |
|
335 | 335 | # resubmit stream |
|
336 | 336 | r = ZMQStream(ctx.socket(zmq.DEALER), loop) |
|
337 | 337 | url = util.disambiguate_url(self.client_url('task')) |
|
338 | 338 | r.connect(url) |
|
339 | 339 | |
|
340 | 340 | self.hub = Hub(loop=loop, session=self.session, monitor=sub, heartmonitor=self.heartmonitor, |
|
341 | 341 | query=q, notifier=n, resubmit=r, db=self.db, |
|
342 | 342 | engine_info=self.engine_info, client_info=self.client_info, |
|
343 | 343 | log=self.log) |
|
344 | 344 | |
|
345 | 345 | |
|
346 | 346 | class Hub(SessionFactory): |
|
347 | 347 | """The IPython Controller Hub with 0MQ connections |
|
348 | 348 | |
|
349 | 349 | Parameters |
|
350 | 350 | ========== |
|
351 | 351 | loop: zmq IOLoop instance |
|
352 | 352 | session: Session object |
|
353 | 353 | <removed> context: zmq context for creating new connections (?) |
|
354 | 354 | queue: ZMQStream for monitoring the command queue (SUB) |
|
355 | 355 | query: ZMQStream for engine registration and client queries requests (ROUTER) |
|
356 | 356 | heartbeat: HeartMonitor object checking the pulse of the engines |
|
357 | 357 | notifier: ZMQStream for broadcasting engine registration changes (PUB) |
|
358 | 358 | db: connection to db for out of memory logging of commands |
|
359 | 359 | NotImplemented |
|
360 | 360 | engine_info: dict of zmq connection information for engines to connect |
|
361 | 361 | to the queues. |
|
362 | 362 | client_info: dict of zmq connection information for engines to connect |
|
363 | 363 | to the queues. |
|
364 | 364 | """ |
|
365 | 365 | |
|
366 | 366 | engine_state_file = Unicode() |
|
367 | 367 | |
|
368 | 368 | # internal data structures: |
|
369 | 369 | ids=Set() # engine IDs |
|
370 | 370 | keytable=Dict() |
|
371 | 371 | by_ident=Dict() |
|
372 | 372 | engines=Dict() |
|
373 | 373 | clients=Dict() |
|
374 | 374 | hearts=Dict() |
|
375 | 375 | pending=Set() |
|
376 | 376 | queues=Dict() # pending msg_ids keyed by engine_id |
|
377 | 377 | tasks=Dict() # pending msg_ids submitted as tasks, keyed by client_id |
|
378 | 378 | completed=Dict() # completed msg_ids keyed by engine_id |
|
379 | 379 | all_completed=Set() # completed msg_ids keyed by engine_id |
|
380 | 380 | dead_engines=Set() # completed msg_ids keyed by engine_id |
|
381 | 381 | unassigned=Set() # set of task msg_ds not yet assigned a destination |
|
382 | 382 | incoming_registrations=Dict() |
|
383 | 383 | registration_timeout=Integer() |
|
384 | 384 | _idcounter=Integer(0) |
|
385 | 385 | |
|
386 | 386 | # objects from constructor: |
|
387 | 387 | query=Instance(ZMQStream) |
|
388 | 388 | monitor=Instance(ZMQStream) |
|
389 | 389 | notifier=Instance(ZMQStream) |
|
390 | 390 | resubmit=Instance(ZMQStream) |
|
391 | 391 | heartmonitor=Instance(HeartMonitor) |
|
392 | 392 | db=Instance(object) |
|
393 | 393 | client_info=Dict() |
|
394 | 394 | engine_info=Dict() |
|
395 | 395 | |
|
396 | 396 | |
|
397 | 397 | def __init__(self, **kwargs): |
|
398 | 398 | """ |
|
399 | 399 | # universal: |
|
400 | 400 | loop: IOLoop for creating future connections |
|
401 | 401 | session: streamsession for sending serialized data |
|
402 | 402 | # engine: |
|
403 | 403 | queue: ZMQStream for monitoring queue messages |
|
404 | 404 | query: ZMQStream for engine+client registration and client requests |
|
405 | 405 | heartbeat: HeartMonitor object for tracking engines |
|
406 | 406 | # extra: |
|
407 | 407 | db: ZMQStream for db connection (NotImplemented) |
|
408 | 408 | engine_info: zmq address/protocol dict for engine connections |
|
409 | 409 | client_info: zmq address/protocol dict for client connections |
|
410 | 410 | """ |
|
411 | 411 | |
|
412 | 412 | super(Hub, self).__init__(**kwargs) |
|
413 | 413 | self.registration_timeout = max(10000, 5*self.heartmonitor.period) |
|
414 | 414 | |
|
415 | 415 | # register our callbacks |
|
416 | 416 | self.query.on_recv(self.dispatch_query) |
|
417 | 417 | self.monitor.on_recv(self.dispatch_monitor_traffic) |
|
418 | 418 | |
|
419 | 419 | self.heartmonitor.add_heart_failure_handler(self.handle_heart_failure) |
|
420 | 420 | self.heartmonitor.add_new_heart_handler(self.handle_new_heart) |
|
421 | 421 | |
|
422 | 422 | self.monitor_handlers = {b'in' : self.save_queue_request, |
|
423 | 423 | b'out': self.save_queue_result, |
|
424 | 424 | b'intask': self.save_task_request, |
|
425 | 425 | b'outtask': self.save_task_result, |
|
426 | 426 | b'tracktask': self.save_task_destination, |
|
427 | 427 | b'incontrol': _passer, |
|
428 | 428 | b'outcontrol': _passer, |
|
429 | 429 | b'iopub': self.save_iopub_message, |
|
430 | 430 | } |
|
431 | 431 | |
|
432 | 432 | self.query_handlers = {'queue_request': self.queue_status, |
|
433 | 433 | 'result_request': self.get_results, |
|
434 | 434 | 'history_request': self.get_history, |
|
435 | 435 | 'db_request': self.db_query, |
|
436 | 436 | 'purge_request': self.purge_results, |
|
437 | 437 | 'load_request': self.check_load, |
|
438 | 438 | 'resubmit_request': self.resubmit_task, |
|
439 | 439 | 'shutdown_request': self.shutdown_request, |
|
440 | 440 | 'registration_request' : self.register_engine, |
|
441 | 441 | 'unregistration_request' : self.unregister_engine, |
|
442 | 442 | 'connection_request': self.connection_request, |
|
443 | 443 | } |
|
444 | 444 | |
|
445 | 445 | # ignore resubmit replies |
|
446 | 446 | self.resubmit.on_recv(lambda msg: None, copy=False) |
|
447 | 447 | |
|
448 | 448 | self.log.info("hub::created hub") |
|
449 | 449 | |
|
450 | 450 | @property |
|
451 | 451 | def _next_id(self): |
|
452 | 452 | """gemerate a new ID. |
|
453 | 453 | |
|
454 | 454 | No longer reuse old ids, just count from 0.""" |
|
455 | 455 | newid = self._idcounter |
|
456 | 456 | self._idcounter += 1 |
|
457 | 457 | return newid |
|
458 | 458 | # newid = 0 |
|
459 | 459 | # incoming = [id[0] for id in itervalues(self.incoming_registrations)] |
|
460 | 460 | # # print newid, self.ids, self.incoming_registrations |
|
461 | 461 | # while newid in self.ids or newid in incoming: |
|
462 | 462 | # newid += 1 |
|
463 | 463 | # return newid |
|
464 | 464 | |
|
465 | 465 | #----------------------------------------------------------------------------- |
|
466 | 466 | # message validation |
|
467 | 467 | #----------------------------------------------------------------------------- |
|
468 | 468 | |
|
469 | 469 | def _validate_targets(self, targets): |
|
470 | 470 | """turn any valid targets argument into a list of integer ids""" |
|
471 | 471 | if targets is None: |
|
472 | 472 | # default to all |
|
473 | 473 | return self.ids |
|
474 | 474 | |
|
475 | 475 | if isinstance(targets, (int,str,unicode_type)): |
|
476 | 476 | # only one target specified |
|
477 | 477 | targets = [targets] |
|
478 | 478 | _targets = [] |
|
479 | 479 | for t in targets: |
|
480 | 480 | # map raw identities to ids |
|
481 | 481 | if isinstance(t, (str,unicode_type)): |
|
482 | 482 | t = self.by_ident.get(cast_bytes(t), t) |
|
483 | 483 | _targets.append(t) |
|
484 | 484 | targets = _targets |
|
485 | 485 | bad_targets = [ t for t in targets if t not in self.ids ] |
|
486 | 486 | if bad_targets: |
|
487 | 487 | raise IndexError("No Such Engine: %r" % bad_targets) |
|
488 | 488 | if not targets: |
|
489 | 489 | raise IndexError("No Engines Registered") |
|
490 | 490 | return targets |
|
491 | 491 | |
|
492 | 492 | #----------------------------------------------------------------------------- |
|
493 | 493 | # dispatch methods (1 per stream) |
|
494 | 494 | #----------------------------------------------------------------------------- |
|
495 | 495 | |
|
496 | 496 | |
|
497 | 497 | @util.log_errors |
|
498 | 498 | def dispatch_monitor_traffic(self, msg): |
|
499 | 499 | """all ME and Task queue messages come through here, as well as |
|
500 | 500 | IOPub traffic.""" |
|
501 | 501 | self.log.debug("monitor traffic: %r", msg[0]) |
|
502 | 502 | switch = msg[0] |
|
503 | 503 | try: |
|
504 | 504 | idents, msg = self.session.feed_identities(msg[1:]) |
|
505 | 505 | except ValueError: |
|
506 | 506 | idents=[] |
|
507 | 507 | if not idents: |
|
508 | 508 | self.log.error("Monitor message without topic: %r", msg) |
|
509 | 509 | return |
|
510 | 510 | handler = self.monitor_handlers.get(switch, None) |
|
511 | 511 | if handler is not None: |
|
512 | 512 | handler(idents, msg) |
|
513 | 513 | else: |
|
514 | 514 | self.log.error("Unrecognized monitor topic: %r", switch) |
|
515 | 515 | |
|
516 | 516 | |
|
517 | 517 | @util.log_errors |
|
518 | 518 | def dispatch_query(self, msg): |
|
519 | 519 | """Route registration requests and queries from clients.""" |
|
520 | 520 | try: |
|
521 | 521 | idents, msg = self.session.feed_identities(msg) |
|
522 | 522 | except ValueError: |
|
523 | 523 | idents = [] |
|
524 | 524 | if not idents: |
|
525 | 525 | self.log.error("Bad Query Message: %r", msg) |
|
526 | 526 | return |
|
527 | 527 | client_id = idents[0] |
|
528 | 528 | try: |
|
529 | 529 | msg = self.session.unserialize(msg, content=True) |
|
530 | 530 | except Exception: |
|
531 | 531 | content = error.wrap_exception() |
|
532 | 532 | self.log.error("Bad Query Message: %r", msg, exc_info=True) |
|
533 | 533 | self.session.send(self.query, "hub_error", ident=client_id, |
|
534 | 534 | content=content) |
|
535 | 535 | return |
|
536 | 536 | # print client_id, header, parent, content |
|
537 | 537 | #switch on message type: |
|
538 | 538 | msg_type = msg['header']['msg_type'] |
|
539 | 539 | self.log.info("client::client %r requested %r", client_id, msg_type) |
|
540 | 540 | handler = self.query_handlers.get(msg_type, None) |
|
541 | 541 | try: |
|
542 | 542 | assert handler is not None, "Bad Message Type: %r" % msg_type |
|
543 | 543 | except: |
|
544 | 544 | content = error.wrap_exception() |
|
545 | 545 | self.log.error("Bad Message Type: %r", msg_type, exc_info=True) |
|
546 | 546 | self.session.send(self.query, "hub_error", ident=client_id, |
|
547 | 547 | content=content) |
|
548 | 548 | return |
|
549 | 549 | |
|
550 | 550 | else: |
|
551 | 551 | handler(idents, msg) |
|
552 | 552 | |
|
553 | 553 | def dispatch_db(self, msg): |
|
554 | 554 | """""" |
|
555 | 555 | raise NotImplementedError |
|
556 | 556 | |
|
557 | 557 | #--------------------------------------------------------------------------- |
|
558 | 558 | # handler methods (1 per event) |
|
559 | 559 | #--------------------------------------------------------------------------- |
|
560 | 560 | |
|
561 | 561 | #----------------------- Heartbeat -------------------------------------- |
|
562 | 562 | |
|
563 | 563 | def handle_new_heart(self, heart): |
|
564 | 564 | """handler to attach to heartbeater. |
|
565 | 565 | Called when a new heart starts to beat. |
|
566 | 566 | Triggers completion of registration.""" |
|
567 | 567 | self.log.debug("heartbeat::handle_new_heart(%r)", heart) |
|
568 | 568 | if heart not in self.incoming_registrations: |
|
569 | 569 | self.log.info("heartbeat::ignoring new heart: %r", heart) |
|
570 | 570 | else: |
|
571 | 571 | self.finish_registration(heart) |
|
572 | 572 | |
|
573 | 573 | |
|
574 | 574 | def handle_heart_failure(self, heart): |
|
575 | 575 | """handler to attach to heartbeater. |
|
576 | 576 | called when a previously registered heart fails to respond to beat request. |
|
577 | 577 | triggers unregistration""" |
|
578 | 578 | self.log.debug("heartbeat::handle_heart_failure(%r)", heart) |
|
579 | 579 | eid = self.hearts.get(heart, None) |
|
580 | 580 | uuid = self.engines[eid].uuid |
|
581 | 581 | if eid is None or self.keytable[eid] in self.dead_engines: |
|
582 | 582 | self.log.info("heartbeat::ignoring heart failure %r (not an engine or already dead)", heart) |
|
583 | 583 | else: |
|
584 | 584 | self.unregister_engine(heart, dict(content=dict(id=eid, queue=uuid))) |
|
585 | 585 | |
|
586 | 586 | #----------------------- MUX Queue Traffic ------------------------------ |
|
587 | 587 | |
|
588 | 588 | def save_queue_request(self, idents, msg): |
|
589 | 589 | if len(idents) < 2: |
|
590 | 590 | self.log.error("invalid identity prefix: %r", idents) |
|
591 | 591 | return |
|
592 | 592 | queue_id, client_id = idents[:2] |
|
593 | 593 | try: |
|
594 | 594 | msg = self.session.unserialize(msg) |
|
595 | 595 | except Exception: |
|
596 | 596 | self.log.error("queue::client %r sent invalid message to %r: %r", client_id, queue_id, msg, exc_info=True) |
|
597 | 597 | return |
|
598 | 598 | |
|
599 | 599 | eid = self.by_ident.get(queue_id, None) |
|
600 | 600 | if eid is None: |
|
601 | 601 | self.log.error("queue::target %r not registered", queue_id) |
|
602 | 602 | self.log.debug("queue:: valid are: %r", self.by_ident.keys()) |
|
603 | 603 | return |
|
604 | 604 | record = init_record(msg) |
|
605 | 605 | msg_id = record['msg_id'] |
|
606 | 606 | self.log.info("queue::client %r submitted request %r to %s", client_id, msg_id, eid) |
|
607 | 607 | # Unicode in records |
|
608 | 608 | record['engine_uuid'] = queue_id.decode('ascii') |
|
609 | 609 | record['client_uuid'] = msg['header']['session'] |
|
610 | 610 | record['queue'] = 'mux' |
|
611 | 611 | |
|
612 | 612 | try: |
|
613 | 613 | # it's posible iopub arrived first: |
|
614 | 614 | existing = self.db.get_record(msg_id) |
|
615 | 615 | for key,evalue in iteritems(existing): |
|
616 | 616 | rvalue = record.get(key, None) |
|
617 | 617 | if evalue and rvalue and evalue != rvalue: |
|
618 | 618 | self.log.warn("conflicting initial state for record: %r:%r <%r> %r", msg_id, rvalue, key, evalue) |
|
619 | 619 | elif evalue and not rvalue: |
|
620 | 620 | record[key] = evalue |
|
621 | 621 | try: |
|
622 | 622 | self.db.update_record(msg_id, record) |
|
623 | 623 | except Exception: |
|
624 | 624 | self.log.error("DB Error updating record %r", msg_id, exc_info=True) |
|
625 | 625 | except KeyError: |
|
626 | 626 | try: |
|
627 | 627 | self.db.add_record(msg_id, record) |
|
628 | 628 | except Exception: |
|
629 | 629 | self.log.error("DB Error adding record %r", msg_id, exc_info=True) |
|
630 | 630 | |
|
631 | 631 | |
|
632 | 632 | self.pending.add(msg_id) |
|
633 | 633 | self.queues[eid].append(msg_id) |
|
634 | 634 | |
|
635 | 635 | def save_queue_result(self, idents, msg): |
|
636 | 636 | if len(idents) < 2: |
|
637 | 637 | self.log.error("invalid identity prefix: %r", idents) |
|
638 | 638 | return |
|
639 | 639 | |
|
640 | 640 | client_id, queue_id = idents[:2] |
|
641 | 641 | try: |
|
642 | 642 | msg = self.session.unserialize(msg) |
|
643 | 643 | except Exception: |
|
644 | 644 | self.log.error("queue::engine %r sent invalid message to %r: %r", |
|
645 | 645 | queue_id, client_id, msg, exc_info=True) |
|
646 | 646 | return |
|
647 | 647 | |
|
648 | 648 | eid = self.by_ident.get(queue_id, None) |
|
649 | 649 | if eid is None: |
|
650 | 650 | self.log.error("queue::unknown engine %r is sending a reply: ", queue_id) |
|
651 | 651 | return |
|
652 | 652 | |
|
653 | 653 | parent = msg['parent_header'] |
|
654 | 654 | if not parent: |
|
655 | 655 | return |
|
656 | 656 | msg_id = parent['msg_id'] |
|
657 | 657 | if msg_id in self.pending: |
|
658 | 658 | self.pending.remove(msg_id) |
|
659 | 659 | self.all_completed.add(msg_id) |
|
660 | 660 | self.queues[eid].remove(msg_id) |
|
661 | 661 | self.completed[eid].append(msg_id) |
|
662 | 662 | self.log.info("queue::request %r completed on %s", msg_id, eid) |
|
663 | 663 | elif msg_id not in self.all_completed: |
|
664 | 664 | # it could be a result from a dead engine that died before delivering the |
|
665 | 665 | # result |
|
666 | 666 | self.log.warn("queue:: unknown msg finished %r", msg_id) |
|
667 | 667 | return |
|
668 | 668 | # update record anyway, because the unregistration could have been premature |
|
669 | 669 | rheader = msg['header'] |
|
670 | 670 | md = msg['metadata'] |
|
671 | 671 | completed = rheader['date'] |
|
672 | 672 | started = md.get('started', None) |
|
673 | 673 | result = { |
|
674 | 674 | 'result_header' : rheader, |
|
675 | 675 | 'result_metadata': md, |
|
676 | 676 | 'result_content': msg['content'], |
|
677 | 677 | 'received': datetime.now(), |
|
678 | 678 | 'started' : started, |
|
679 | 679 | 'completed' : completed |
|
680 | 680 | } |
|
681 | 681 | |
|
682 | 682 | result['result_buffers'] = msg['buffers'] |
|
683 | 683 | try: |
|
684 | 684 | self.db.update_record(msg_id, result) |
|
685 | 685 | except Exception: |
|
686 | 686 | self.log.error("DB Error updating record %r", msg_id, exc_info=True) |
|
687 | 687 | |
|
688 | 688 | |
|
689 | 689 | #--------------------- Task Queue Traffic ------------------------------ |
|
690 | 690 | |
|
691 | 691 | def save_task_request(self, idents, msg): |
|
692 | 692 | """Save the submission of a task.""" |
|
693 | 693 | client_id = idents[0] |
|
694 | 694 | |
|
695 | 695 | try: |
|
696 | 696 | msg = self.session.unserialize(msg) |
|
697 | 697 | except Exception: |
|
698 | 698 | self.log.error("task::client %r sent invalid task message: %r", |
|
699 | 699 | client_id, msg, exc_info=True) |
|
700 | 700 | return |
|
701 | 701 | record = init_record(msg) |
|
702 | 702 | |
|
703 | 703 | record['client_uuid'] = msg['header']['session'] |
|
704 | 704 | record['queue'] = 'task' |
|
705 | 705 | header = msg['header'] |
|
706 | 706 | msg_id = header['msg_id'] |
|
707 | 707 | self.pending.add(msg_id) |
|
708 | 708 | self.unassigned.add(msg_id) |
|
709 | 709 | try: |
|
710 | 710 | # it's posible iopub arrived first: |
|
711 | 711 | existing = self.db.get_record(msg_id) |
|
712 | 712 | if existing['resubmitted']: |
|
713 | 713 | for key in ('submitted', 'client_uuid', 'buffers'): |
|
714 | 714 | # don't clobber these keys on resubmit |
|
715 | 715 | # submitted and client_uuid should be different |
|
716 | 716 | # and buffers might be big, and shouldn't have changed |
|
717 | 717 | record.pop(key) |
|
718 | 718 | # still check content,header which should not change |
|
719 | 719 | # but are not expensive to compare as buffers |
|
720 | 720 | |
|
721 | 721 | for key,evalue in iteritems(existing): |
|
722 | 722 | if key.endswith('buffers'): |
|
723 | 723 | # don't compare buffers |
|
724 | 724 | continue |
|
725 | 725 | rvalue = record.get(key, None) |
|
726 | 726 | if evalue and rvalue and evalue != rvalue: |
|
727 | 727 | self.log.warn("conflicting initial state for record: %r:%r <%r> %r", msg_id, rvalue, key, evalue) |
|
728 | 728 | elif evalue and not rvalue: |
|
729 | 729 | record[key] = evalue |
|
730 | 730 | try: |
|
731 | 731 | self.db.update_record(msg_id, record) |
|
732 | 732 | except Exception: |
|
733 | 733 | self.log.error("DB Error updating record %r", msg_id, exc_info=True) |
|
734 | 734 | except KeyError: |
|
735 | 735 | try: |
|
736 | 736 | self.db.add_record(msg_id, record) |
|
737 | 737 | except Exception: |
|
738 | 738 | self.log.error("DB Error adding record %r", msg_id, exc_info=True) |
|
739 | 739 | except Exception: |
|
740 | 740 | self.log.error("DB Error saving task request %r", msg_id, exc_info=True) |
|
741 | 741 | |
|
742 | 742 | def save_task_result(self, idents, msg): |
|
743 | 743 | """save the result of a completed task.""" |
|
744 | 744 | client_id = idents[0] |
|
745 | 745 | try: |
|
746 | 746 | msg = self.session.unserialize(msg) |
|
747 | 747 | except Exception: |
|
748 | 748 | self.log.error("task::invalid task result message send to %r: %r", |
|
749 | 749 | client_id, msg, exc_info=True) |
|
750 | 750 | return |
|
751 | 751 | |
|
752 | 752 | parent = msg['parent_header'] |
|
753 | 753 | if not parent: |
|
754 | 754 | # print msg |
|
755 | 755 | self.log.warn("Task %r had no parent!", msg) |
|
756 | 756 | return |
|
757 | 757 | msg_id = parent['msg_id'] |
|
758 | 758 | if msg_id in self.unassigned: |
|
759 | 759 | self.unassigned.remove(msg_id) |
|
760 | 760 | |
|
761 | 761 | header = msg['header'] |
|
762 | 762 | md = msg['metadata'] |
|
763 | 763 | engine_uuid = md.get('engine', u'') |
|
764 | 764 | eid = self.by_ident.get(cast_bytes(engine_uuid), None) |
|
765 | 765 | |
|
766 | 766 | status = md.get('status', None) |
|
767 | 767 | |
|
768 | 768 | if msg_id in self.pending: |
|
769 | 769 | self.log.info("task::task %r finished on %s", msg_id, eid) |
|
770 | 770 | self.pending.remove(msg_id) |
|
771 | 771 | self.all_completed.add(msg_id) |
|
772 | 772 | if eid is not None: |
|
773 | 773 | if status != 'aborted': |
|
774 | 774 | self.completed[eid].append(msg_id) |
|
775 | 775 | if msg_id in self.tasks[eid]: |
|
776 | 776 | self.tasks[eid].remove(msg_id) |
|
777 | 777 | completed = header['date'] |
|
778 | 778 | started = md.get('started', None) |
|
779 | 779 | result = { |
|
780 | 780 | 'result_header' : header, |
|
781 | 781 | 'result_metadata': msg['metadata'], |
|
782 | 782 | 'result_content': msg['content'], |
|
783 | 783 | 'started' : started, |
|
784 | 784 | 'completed' : completed, |
|
785 | 785 | 'received' : datetime.now(), |
|
786 | 786 | 'engine_uuid': engine_uuid, |
|
787 | 787 | } |
|
788 | 788 | |
|
789 | 789 | result['result_buffers'] = msg['buffers'] |
|
790 | 790 | try: |
|
791 | 791 | self.db.update_record(msg_id, result) |
|
792 | 792 | except Exception: |
|
793 | 793 | self.log.error("DB Error saving task request %r", msg_id, exc_info=True) |
|
794 | 794 | |
|
795 | 795 | else: |
|
796 | 796 | self.log.debug("task::unknown task %r finished", msg_id) |
|
797 | 797 | |
|
798 | 798 | def save_task_destination(self, idents, msg): |
|
799 | 799 | try: |
|
800 | 800 | msg = self.session.unserialize(msg, content=True) |
|
801 | 801 | except Exception: |
|
802 | 802 | self.log.error("task::invalid task tracking message", exc_info=True) |
|
803 | 803 | return |
|
804 | 804 | content = msg['content'] |
|
805 | 805 | # print (content) |
|
806 | 806 | msg_id = content['msg_id'] |
|
807 | 807 | engine_uuid = content['engine_id'] |
|
808 | 808 | eid = self.by_ident[cast_bytes(engine_uuid)] |
|
809 | 809 | |
|
810 | 810 | self.log.info("task::task %r arrived on %r", msg_id, eid) |
|
811 | 811 | if msg_id in self.unassigned: |
|
812 | 812 | self.unassigned.remove(msg_id) |
|
813 | 813 | # else: |
|
814 | 814 | # self.log.debug("task::task %r not listed as MIA?!"%(msg_id)) |
|
815 | 815 | |
|
816 | 816 | self.tasks[eid].append(msg_id) |
|
817 | 817 | # self.pending[msg_id][1].update(received=datetime.now(),engine=(eid,engine_uuid)) |
|
818 | 818 | try: |
|
819 | 819 | self.db.update_record(msg_id, dict(engine_uuid=engine_uuid)) |
|
820 | 820 | except Exception: |
|
821 | 821 | self.log.error("DB Error saving task destination %r", msg_id, exc_info=True) |
|
822 | 822 | |
|
823 | 823 | |
|
824 | 824 | def mia_task_request(self, idents, msg): |
|
825 | 825 | raise NotImplementedError |
|
826 | 826 | client_id = idents[0] |
|
827 | 827 | # content = dict(mia=self.mia,status='ok') |
|
828 | 828 | # self.session.send('mia_reply', content=content, idents=client_id) |
|
829 | 829 | |
|
830 | 830 | |
|
831 | 831 | #--------------------- IOPub Traffic ------------------------------ |
|
832 | 832 | |
|
833 | 833 | def save_iopub_message(self, topics, msg): |
|
834 | 834 | """save an iopub message into the db""" |
|
835 | 835 | # print (topics) |
|
836 | 836 | try: |
|
837 | 837 | msg = self.session.unserialize(msg, content=True) |
|
838 | 838 | except Exception: |
|
839 | 839 | self.log.error("iopub::invalid IOPub message", exc_info=True) |
|
840 | 840 | return |
|
841 | 841 | |
|
842 | 842 | parent = msg['parent_header'] |
|
843 | 843 | if not parent: |
|
844 | 844 | self.log.warn("iopub::IOPub message lacks parent: %r", msg) |
|
845 | 845 | return |
|
846 | 846 | msg_id = parent['msg_id'] |
|
847 | 847 | msg_type = msg['header']['msg_type'] |
|
848 | 848 | content = msg['content'] |
|
849 | 849 | |
|
850 | 850 | # ensure msg_id is in db |
|
851 | 851 | try: |
|
852 | 852 | rec = self.db.get_record(msg_id) |
|
853 | 853 | except KeyError: |
|
854 | 854 | rec = empty_record() |
|
855 | 855 | rec['msg_id'] = msg_id |
|
856 | 856 | self.db.add_record(msg_id, rec) |
|
857 | 857 | # stream |
|
858 | 858 | d = {} |
|
859 | 859 | if msg_type == 'stream': |
|
860 | 860 | name = content['name'] |
|
861 | 861 | s = rec[name] or '' |
|
862 | 862 | d[name] = s + content['data'] |
|
863 | 863 | |
|
864 | 864 | elif msg_type == 'pyerr': |
|
865 | 865 | d['pyerr'] = content |
|
866 | 866 | elif msg_type == 'pyin': |
|
867 | 867 | d['pyin'] = content['code'] |
|
868 | 868 | elif msg_type in ('display_data', 'pyout'): |
|
869 | 869 | d[msg_type] = content |
|
870 | 870 | elif msg_type == 'status': |
|
871 | 871 | pass |
|
872 | 872 | elif msg_type == 'data_pub': |
|
873 | 873 | self.log.info("ignored data_pub message for %s" % msg_id) |
|
874 | 874 | else: |
|
875 | 875 | self.log.warn("unhandled iopub msg_type: %r", msg_type) |
|
876 | 876 | |
|
877 | 877 | if not d: |
|
878 | 878 | return |
|
879 | 879 | |
|
880 | 880 | try: |
|
881 | 881 | self.db.update_record(msg_id, d) |
|
882 | 882 | except Exception: |
|
883 | 883 | self.log.error("DB Error saving iopub message %r", msg_id, exc_info=True) |
|
884 | 884 | |
|
885 | 885 | |
|
886 | 886 | |
|
887 | 887 | #------------------------------------------------------------------------- |
|
888 | 888 | # Registration requests |
|
889 | 889 | #------------------------------------------------------------------------- |
|
890 | 890 | |
|
891 | 891 | def connection_request(self, client_id, msg): |
|
892 | 892 | """Reply with connection addresses for clients.""" |
|
893 | 893 | self.log.info("client::client %r connected", client_id) |
|
894 | 894 | content = dict(status='ok') |
|
895 | 895 | jsonable = {} |
|
896 | 896 | for k,v in iteritems(self.keytable): |
|
897 | 897 | if v not in self.dead_engines: |
|
898 | 898 | jsonable[str(k)] = v |
|
899 | 899 | content['engines'] = jsonable |
|
900 | 900 | self.session.send(self.query, 'connection_reply', content, parent=msg, ident=client_id) |
|
901 | 901 | |
|
902 | 902 | def register_engine(self, reg, msg): |
|
903 | 903 | """Register a new engine.""" |
|
904 | 904 | content = msg['content'] |
|
905 | 905 | try: |
|
906 | 906 | uuid = content['uuid'] |
|
907 | 907 | except KeyError: |
|
908 | 908 | self.log.error("registration::queue not specified", exc_info=True) |
|
909 | 909 | return |
|
910 | 910 | |
|
911 | 911 | eid = self._next_id |
|
912 | 912 | |
|
913 | 913 | self.log.debug("registration::register_engine(%i, %r)", eid, uuid) |
|
914 | 914 | |
|
915 | 915 | content = dict(id=eid,status='ok',hb_period=self.heartmonitor.period) |
|
916 | 916 | # check if requesting available IDs: |
|
917 | 917 | if cast_bytes(uuid) in self.by_ident: |
|
918 | 918 | try: |
|
919 | 919 | raise KeyError("uuid %r in use" % uuid) |
|
920 | 920 | except: |
|
921 | 921 | content = error.wrap_exception() |
|
922 | 922 | self.log.error("uuid %r in use", uuid, exc_info=True) |
|
923 | 923 | else: |
|
924 | 924 | for h, ec in iteritems(self.incoming_registrations): |
|
925 | 925 | if uuid == h: |
|
926 | 926 | try: |
|
927 | 927 | raise KeyError("heart_id %r in use" % uuid) |
|
928 | 928 | except: |
|
929 | 929 | self.log.error("heart_id %r in use", uuid, exc_info=True) |
|
930 | 930 | content = error.wrap_exception() |
|
931 | 931 | break |
|
932 | 932 | elif uuid == ec.uuid: |
|
933 | 933 | try: |
|
934 | 934 | raise KeyError("uuid %r in use" % uuid) |
|
935 | 935 | except: |
|
936 | 936 | self.log.error("uuid %r in use", uuid, exc_info=True) |
|
937 | 937 | content = error.wrap_exception() |
|
938 | 938 | break |
|
939 | 939 | |
|
940 | 940 | msg = self.session.send(self.query, "registration_reply", |
|
941 | 941 | content=content, |
|
942 | 942 | ident=reg) |
|
943 | 943 | |
|
944 | 944 | heart = cast_bytes(uuid) |
|
945 | 945 | |
|
946 | 946 | if content['status'] == 'ok': |
|
947 | 947 | if heart in self.heartmonitor.hearts: |
|
948 | 948 | # already beating |
|
949 | 949 | self.incoming_registrations[heart] = EngineConnector(id=eid,uuid=uuid) |
|
950 | 950 | self.finish_registration(heart) |
|
951 | 951 | else: |
|
952 | 952 | purge = lambda : self._purge_stalled_registration(heart) |
|
953 | 953 | dc = ioloop.DelayedCallback(purge, self.registration_timeout, self.loop) |
|
954 | 954 | dc.start() |
|
955 | 955 | self.incoming_registrations[heart] = EngineConnector(id=eid,uuid=uuid,stallback=dc) |
|
956 | 956 | else: |
|
957 | 957 | self.log.error("registration::registration %i failed: %r", eid, content['evalue']) |
|
958 | 958 | |
|
959 | 959 | return eid |
|
960 | 960 | |
|
961 | 961 | def unregister_engine(self, ident, msg): |
|
962 | 962 | """Unregister an engine that explicitly requested to leave.""" |
|
963 | 963 | try: |
|
964 | 964 | eid = msg['content']['id'] |
|
965 | 965 | except: |
|
966 | 966 | self.log.error("registration::bad engine id for unregistration: %r", ident, exc_info=True) |
|
967 | 967 | return |
|
968 | 968 | self.log.info("registration::unregister_engine(%r)", eid) |
|
969 | 969 | # print (eid) |
|
970 | 970 | uuid = self.keytable[eid] |
|
971 | 971 | content=dict(id=eid, uuid=uuid) |
|
972 | 972 | self.dead_engines.add(uuid) |
|
973 | 973 | # self.ids.remove(eid) |
|
974 | 974 | # uuid = self.keytable.pop(eid) |
|
975 | 975 | # |
|
976 | 976 | # ec = self.engines.pop(eid) |
|
977 | 977 | # self.hearts.pop(ec.heartbeat) |
|
978 | 978 | # self.by_ident.pop(ec.queue) |
|
979 | 979 | # self.completed.pop(eid) |
|
980 | 980 | handleit = lambda : self._handle_stranded_msgs(eid, uuid) |
|
981 | 981 | dc = ioloop.DelayedCallback(handleit, self.registration_timeout, self.loop) |
|
982 | 982 | dc.start() |
|
983 | 983 | ############## TODO: HANDLE IT ################ |
|
984 | 984 | |
|
985 | 985 | self._save_engine_state() |
|
986 | 986 | |
|
987 | 987 | if self.notifier: |
|
988 | 988 | self.session.send(self.notifier, "unregistration_notification", content=content) |
|
989 | 989 | |
|
990 | 990 | def _handle_stranded_msgs(self, eid, uuid): |
|
991 | 991 | """Handle messages known to be on an engine when the engine unregisters. |
|
992 | 992 | |
|
993 | 993 | It is possible that this will fire prematurely - that is, an engine will |
|
994 | 994 | go down after completing a result, and the client will be notified |
|
995 | 995 | that the result failed and later receive the actual result. |
|
996 | 996 | """ |
|
997 | 997 | |
|
998 | 998 | outstanding = self.queues[eid] |
|
999 | 999 | |
|
1000 | 1000 | for msg_id in outstanding: |
|
1001 | 1001 | self.pending.remove(msg_id) |
|
1002 | 1002 | self.all_completed.add(msg_id) |
|
1003 | 1003 | try: |
|
1004 | 1004 | raise error.EngineError("Engine %r died while running task %r" % (eid, msg_id)) |
|
1005 | 1005 | except: |
|
1006 | 1006 | content = error.wrap_exception() |
|
1007 | 1007 | # build a fake header: |
|
1008 | 1008 | header = {} |
|
1009 | 1009 | header['engine'] = uuid |
|
1010 | 1010 | header['date'] = datetime.now() |
|
1011 | 1011 | rec = dict(result_content=content, result_header=header, result_buffers=[]) |
|
1012 | 1012 | rec['completed'] = header['date'] |
|
1013 | 1013 | rec['engine_uuid'] = uuid |
|
1014 | 1014 | try: |
|
1015 | 1015 | self.db.update_record(msg_id, rec) |
|
1016 | 1016 | except Exception: |
|
1017 | 1017 | self.log.error("DB Error handling stranded msg %r", msg_id, exc_info=True) |
|
1018 | 1018 | |
|
1019 | 1019 | |
|
1020 | 1020 | def finish_registration(self, heart): |
|
1021 | 1021 | """Second half of engine registration, called after our HeartMonitor |
|
1022 | 1022 | has received a beat from the Engine's Heart.""" |
|
1023 | 1023 | try: |
|
1024 | 1024 | ec = self.incoming_registrations.pop(heart) |
|
1025 | 1025 | except KeyError: |
|
1026 | 1026 | self.log.error("registration::tried to finish nonexistant registration", exc_info=True) |
|
1027 | 1027 | return |
|
1028 | 1028 | self.log.info("registration::finished registering engine %i:%s", ec.id, ec.uuid) |
|
1029 | 1029 | if ec.stallback is not None: |
|
1030 | 1030 | ec.stallback.stop() |
|
1031 | 1031 | eid = ec.id |
|
1032 | 1032 | self.ids.add(eid) |
|
1033 | 1033 | self.keytable[eid] = ec.uuid |
|
1034 | 1034 | self.engines[eid] = ec |
|
1035 | 1035 | self.by_ident[cast_bytes(ec.uuid)] = ec.id |
|
1036 | 1036 | self.queues[eid] = list() |
|
1037 | 1037 | self.tasks[eid] = list() |
|
1038 | 1038 | self.completed[eid] = list() |
|
1039 | 1039 | self.hearts[heart] = eid |
|
1040 | 1040 | content = dict(id=eid, uuid=self.engines[eid].uuid) |
|
1041 | 1041 | if self.notifier: |
|
1042 | 1042 | self.session.send(self.notifier, "registration_notification", content=content) |
|
1043 | 1043 | self.log.info("engine::Engine Connected: %i", eid) |
|
1044 | 1044 | |
|
1045 | 1045 | self._save_engine_state() |
|
1046 | 1046 | |
|
1047 | 1047 | def _purge_stalled_registration(self, heart): |
|
1048 | 1048 | if heart in self.incoming_registrations: |
|
1049 | 1049 | ec = self.incoming_registrations.pop(heart) |
|
1050 | 1050 | self.log.info("registration::purging stalled registration: %i", ec.id) |
|
1051 | 1051 | else: |
|
1052 | 1052 | pass |
|
1053 | 1053 | |
|
1054 | 1054 | #------------------------------------------------------------------------- |
|
1055 | 1055 | # Engine State |
|
1056 | 1056 | #------------------------------------------------------------------------- |
|
1057 | 1057 | |
|
1058 | 1058 | |
|
1059 | 1059 | def _cleanup_engine_state_file(self): |
|
1060 | 1060 | """cleanup engine state mapping""" |
|
1061 | 1061 | |
|
1062 | 1062 | if os.path.exists(self.engine_state_file): |
|
1063 | 1063 | self.log.debug("cleaning up engine state: %s", self.engine_state_file) |
|
1064 | 1064 | try: |
|
1065 | 1065 | os.remove(self.engine_state_file) |
|
1066 | 1066 | except IOError: |
|
1067 | 1067 | self.log.error("Couldn't cleanup file: %s", self.engine_state_file, exc_info=True) |
|
1068 | 1068 | |
|
1069 | 1069 | |
|
1070 | 1070 | def _save_engine_state(self): |
|
1071 | 1071 | """save engine mapping to JSON file""" |
|
1072 | 1072 | if not self.engine_state_file: |
|
1073 | 1073 | return |
|
1074 | 1074 | self.log.debug("save engine state to %s" % self.engine_state_file) |
|
1075 | 1075 | state = {} |
|
1076 | 1076 | engines = {} |
|
1077 | 1077 | for eid, ec in iteritems(self.engines): |
|
1078 | 1078 | if ec.uuid not in self.dead_engines: |
|
1079 | 1079 | engines[eid] = ec.uuid |
|
1080 | 1080 | |
|
1081 | 1081 | state['engines'] = engines |
|
1082 | 1082 | |
|
1083 | 1083 | state['next_id'] = self._idcounter |
|
1084 | 1084 | |
|
1085 | 1085 | with open(self.engine_state_file, 'w') as f: |
|
1086 | 1086 | json.dump(state, f) |
|
1087 | 1087 | |
|
1088 | 1088 | |
|
1089 | 1089 | def _load_engine_state(self): |
|
1090 | 1090 | """load engine mapping from JSON file""" |
|
1091 | 1091 | if not os.path.exists(self.engine_state_file): |
|
1092 | 1092 | return |
|
1093 | 1093 | |
|
1094 | 1094 | self.log.info("loading engine state from %s" % self.engine_state_file) |
|
1095 | 1095 | |
|
1096 | 1096 | with open(self.engine_state_file) as f: |
|
1097 | 1097 | state = json.load(f) |
|
1098 | 1098 | |
|
1099 | 1099 | save_notifier = self.notifier |
|
1100 | 1100 | self.notifier = None |
|
1101 | 1101 | for eid, uuid in iteritems(state['engines']): |
|
1102 | 1102 | heart = uuid.encode('ascii') |
|
1103 | 1103 | # start with this heart as current and beating: |
|
1104 | 1104 | self.heartmonitor.responses.add(heart) |
|
1105 | 1105 | self.heartmonitor.hearts.add(heart) |
|
1106 | 1106 | |
|
1107 | 1107 | self.incoming_registrations[heart] = EngineConnector(id=int(eid), uuid=uuid) |
|
1108 | 1108 | self.finish_registration(heart) |
|
1109 | 1109 | |
|
1110 | 1110 | self.notifier = save_notifier |
|
1111 | 1111 | |
|
1112 | 1112 | self._idcounter = state['next_id'] |
|
1113 | 1113 | |
|
1114 | 1114 | #------------------------------------------------------------------------- |
|
1115 | 1115 | # Client Requests |
|
1116 | 1116 | #------------------------------------------------------------------------- |
|
1117 | 1117 | |
|
1118 | 1118 | def shutdown_request(self, client_id, msg): |
|
1119 | 1119 | """handle shutdown request.""" |
|
1120 | 1120 | self.session.send(self.query, 'shutdown_reply', content={'status': 'ok'}, ident=client_id) |
|
1121 | 1121 | # also notify other clients of shutdown |
|
1122 | 1122 | self.session.send(self.notifier, 'shutdown_notice', content={'status': 'ok'}) |
|
1123 | 1123 | dc = ioloop.DelayedCallback(lambda : self._shutdown(), 1000, self.loop) |
|
1124 | 1124 | dc.start() |
|
1125 | 1125 | |
|
1126 | 1126 | def _shutdown(self): |
|
1127 | 1127 | self.log.info("hub::hub shutting down.") |
|
1128 | 1128 | time.sleep(0.1) |
|
1129 | 1129 | sys.exit(0) |
|
1130 | 1130 | |
|
1131 | 1131 | |
|
1132 | 1132 | def check_load(self, client_id, msg): |
|
1133 | 1133 | content = msg['content'] |
|
1134 | 1134 | try: |
|
1135 | 1135 | targets = content['targets'] |
|
1136 | 1136 | targets = self._validate_targets(targets) |
|
1137 | 1137 | except: |
|
1138 | 1138 | content = error.wrap_exception() |
|
1139 | 1139 | self.session.send(self.query, "hub_error", |
|
1140 | 1140 | content=content, ident=client_id) |
|
1141 | 1141 | return |
|
1142 | 1142 | |
|
1143 | 1143 | content = dict(status='ok') |
|
1144 | 1144 | # loads = {} |
|
1145 | 1145 | for t in targets: |
|
1146 | 1146 | content[bytes(t)] = len(self.queues[t])+len(self.tasks[t]) |
|
1147 | 1147 | self.session.send(self.query, "load_reply", content=content, ident=client_id) |
|
1148 | 1148 | |
|
1149 | 1149 | |
|
1150 | 1150 | def queue_status(self, client_id, msg): |
|
1151 | 1151 | """Return the Queue status of one or more targets. |
|
1152 | if verbose: return the msg_ids | |
|
1153 |
else |
|
|
1154 | keys: queue (pending MUX jobs) | |
|
1155 | tasks (pending Task jobs) | |
|
1156 | completed (finished jobs from both queues)""" | |
|
1152 | ||
|
1153 | If verbose, return the msg_ids, else return len of each type. | |
|
1154 | ||
|
1155 | Keys: | |
|
1156 | ||
|
1157 | * queue (pending MUX jobs) | |
|
1158 | * tasks (pending Task jobs) | |
|
1159 | * completed (finished jobs from both queues) | |
|
1160 | """ | |
|
1157 | 1161 | content = msg['content'] |
|
1158 | 1162 | targets = content['targets'] |
|
1159 | 1163 | try: |
|
1160 | 1164 | targets = self._validate_targets(targets) |
|
1161 | 1165 | except: |
|
1162 | 1166 | content = error.wrap_exception() |
|
1163 | 1167 | self.session.send(self.query, "hub_error", |
|
1164 | 1168 | content=content, ident=client_id) |
|
1165 | 1169 | return |
|
1166 | 1170 | verbose = content.get('verbose', False) |
|
1167 | 1171 | content = dict(status='ok') |
|
1168 | 1172 | for t in targets: |
|
1169 | 1173 | queue = self.queues[t] |
|
1170 | 1174 | completed = self.completed[t] |
|
1171 | 1175 | tasks = self.tasks[t] |
|
1172 | 1176 | if not verbose: |
|
1173 | 1177 | queue = len(queue) |
|
1174 | 1178 | completed = len(completed) |
|
1175 | 1179 | tasks = len(tasks) |
|
1176 | 1180 | content[str(t)] = {'queue': queue, 'completed': completed , 'tasks': tasks} |
|
1177 | 1181 | content['unassigned'] = list(self.unassigned) if verbose else len(self.unassigned) |
|
1178 | 1182 | # print (content) |
|
1179 | 1183 | self.session.send(self.query, "queue_reply", content=content, ident=client_id) |
|
1180 | 1184 | |
|
1181 | 1185 | def purge_results(self, client_id, msg): |
|
1182 | 1186 | """Purge results from memory. This method is more valuable before we move |
|
1183 | 1187 | to a DB based message storage mechanism.""" |
|
1184 | 1188 | content = msg['content'] |
|
1185 | 1189 | self.log.info("Dropping records with %s", content) |
|
1186 | 1190 | msg_ids = content.get('msg_ids', []) |
|
1187 | 1191 | reply = dict(status='ok') |
|
1188 | 1192 | if msg_ids == 'all': |
|
1189 | 1193 | try: |
|
1190 | 1194 | self.db.drop_matching_records(dict(completed={'$ne':None})) |
|
1191 | 1195 | except Exception: |
|
1192 | 1196 | reply = error.wrap_exception() |
|
1193 | 1197 | else: |
|
1194 | 1198 | pending = [m for m in msg_ids if (m in self.pending)] |
|
1195 | 1199 | if pending: |
|
1196 | 1200 | try: |
|
1197 | 1201 | raise IndexError("msg pending: %r" % pending[0]) |
|
1198 | 1202 | except: |
|
1199 | 1203 | reply = error.wrap_exception() |
|
1200 | 1204 | else: |
|
1201 | 1205 | try: |
|
1202 | 1206 | self.db.drop_matching_records(dict(msg_id={'$in':msg_ids})) |
|
1203 | 1207 | except Exception: |
|
1204 | 1208 | reply = error.wrap_exception() |
|
1205 | 1209 | |
|
1206 | 1210 | if reply['status'] == 'ok': |
|
1207 | 1211 | eids = content.get('engine_ids', []) |
|
1208 | 1212 | for eid in eids: |
|
1209 | 1213 | if eid not in self.engines: |
|
1210 | 1214 | try: |
|
1211 | 1215 | raise IndexError("No such engine: %i" % eid) |
|
1212 | 1216 | except: |
|
1213 | 1217 | reply = error.wrap_exception() |
|
1214 | 1218 | break |
|
1215 | 1219 | uid = self.engines[eid].uuid |
|
1216 | 1220 | try: |
|
1217 | 1221 | self.db.drop_matching_records(dict(engine_uuid=uid, completed={'$ne':None})) |
|
1218 | 1222 | except Exception: |
|
1219 | 1223 | reply = error.wrap_exception() |
|
1220 | 1224 | break |
|
1221 | 1225 | |
|
1222 | 1226 | self.session.send(self.query, 'purge_reply', content=reply, ident=client_id) |
|
1223 | 1227 | |
|
1224 | 1228 | def resubmit_task(self, client_id, msg): |
|
1225 | 1229 | """Resubmit one or more tasks.""" |
|
1226 | 1230 | def finish(reply): |
|
1227 | 1231 | self.session.send(self.query, 'resubmit_reply', content=reply, ident=client_id) |
|
1228 | 1232 | |
|
1229 | 1233 | content = msg['content'] |
|
1230 | 1234 | msg_ids = content['msg_ids'] |
|
1231 | 1235 | reply = dict(status='ok') |
|
1232 | 1236 | try: |
|
1233 | 1237 | records = self.db.find_records({'msg_id' : {'$in' : msg_ids}}, keys=[ |
|
1234 | 1238 | 'header', 'content', 'buffers']) |
|
1235 | 1239 | except Exception: |
|
1236 | 1240 | self.log.error('db::db error finding tasks to resubmit', exc_info=True) |
|
1237 | 1241 | return finish(error.wrap_exception()) |
|
1238 | 1242 | |
|
1239 | 1243 | # validate msg_ids |
|
1240 | 1244 | found_ids = [ rec['msg_id'] for rec in records ] |
|
1241 | 1245 | pending_ids = [ msg_id for msg_id in found_ids if msg_id in self.pending ] |
|
1242 | 1246 | if len(records) > len(msg_ids): |
|
1243 | 1247 | try: |
|
1244 | 1248 | raise RuntimeError("DB appears to be in an inconsistent state." |
|
1245 | 1249 | "More matching records were found than should exist") |
|
1246 | 1250 | except Exception: |
|
1247 | 1251 | return finish(error.wrap_exception()) |
|
1248 | 1252 | elif len(records) < len(msg_ids): |
|
1249 | 1253 | missing = [ m for m in msg_ids if m not in found_ids ] |
|
1250 | 1254 | try: |
|
1251 | 1255 | raise KeyError("No such msg(s): %r" % missing) |
|
1252 | 1256 | except KeyError: |
|
1253 | 1257 | return finish(error.wrap_exception()) |
|
1254 | 1258 | elif pending_ids: |
|
1255 | 1259 | pass |
|
1256 | 1260 | # no need to raise on resubmit of pending task, now that we |
|
1257 | 1261 | # resubmit under new ID, but do we want to raise anyway? |
|
1258 | 1262 | # msg_id = invalid_ids[0] |
|
1259 | 1263 | # try: |
|
1260 | 1264 | # raise ValueError("Task(s) %r appears to be inflight" % ) |
|
1261 | 1265 | # except Exception: |
|
1262 | 1266 | # return finish(error.wrap_exception()) |
|
1263 | 1267 | |
|
1264 | 1268 | # mapping of original IDs to resubmitted IDs |
|
1265 | 1269 | resubmitted = {} |
|
1266 | 1270 | |
|
1267 | 1271 | # send the messages |
|
1268 | 1272 | for rec in records: |
|
1269 | 1273 | header = rec['header'] |
|
1270 | 1274 | msg = self.session.msg(header['msg_type'], parent=header) |
|
1271 | 1275 | msg_id = msg['msg_id'] |
|
1272 | 1276 | msg['content'] = rec['content'] |
|
1273 | 1277 | |
|
1274 | 1278 | # use the old header, but update msg_id and timestamp |
|
1275 | 1279 | fresh = msg['header'] |
|
1276 | 1280 | header['msg_id'] = fresh['msg_id'] |
|
1277 | 1281 | header['date'] = fresh['date'] |
|
1278 | 1282 | msg['header'] = header |
|
1279 | 1283 | |
|
1280 | 1284 | self.session.send(self.resubmit, msg, buffers=rec['buffers']) |
|
1281 | 1285 | |
|
1282 | 1286 | resubmitted[rec['msg_id']] = msg_id |
|
1283 | 1287 | self.pending.add(msg_id) |
|
1284 | 1288 | msg['buffers'] = rec['buffers'] |
|
1285 | 1289 | try: |
|
1286 | 1290 | self.db.add_record(msg_id, init_record(msg)) |
|
1287 | 1291 | except Exception: |
|
1288 | 1292 | self.log.error("db::DB Error updating record: %s", msg_id, exc_info=True) |
|
1289 | 1293 | return finish(error.wrap_exception()) |
|
1290 | 1294 | |
|
1291 | 1295 | finish(dict(status='ok', resubmitted=resubmitted)) |
|
1292 | 1296 | |
|
1293 | 1297 | # store the new IDs in the Task DB |
|
1294 | 1298 | for msg_id, resubmit_id in iteritems(resubmitted): |
|
1295 | 1299 | try: |
|
1296 | 1300 | self.db.update_record(msg_id, {'resubmitted' : resubmit_id}) |
|
1297 | 1301 | except Exception: |
|
1298 | 1302 | self.log.error("db::DB Error updating record: %s", msg_id, exc_info=True) |
|
1299 | 1303 | |
|
1300 | 1304 | |
|
1301 | 1305 | def _extract_record(self, rec): |
|
1302 | 1306 | """decompose a TaskRecord dict into subsection of reply for get_result""" |
|
1303 | 1307 | io_dict = {} |
|
1304 | 1308 | for key in ('pyin', 'pyout', 'pyerr', 'stdout', 'stderr'): |
|
1305 | 1309 | io_dict[key] = rec[key] |
|
1306 | 1310 | content = { |
|
1307 | 1311 | 'header': rec['header'], |
|
1308 | 1312 | 'metadata': rec['metadata'], |
|
1309 | 1313 | 'result_metadata': rec['result_metadata'], |
|
1310 | 1314 | 'result_header' : rec['result_header'], |
|
1311 | 1315 | 'result_content': rec['result_content'], |
|
1312 | 1316 | 'received' : rec['received'], |
|
1313 | 1317 | 'io' : io_dict, |
|
1314 | 1318 | } |
|
1315 | 1319 | if rec['result_buffers']: |
|
1316 | 1320 | buffers = list(map(bytes, rec['result_buffers'])) |
|
1317 | 1321 | else: |
|
1318 | 1322 | buffers = [] |
|
1319 | 1323 | |
|
1320 | 1324 | return content, buffers |
|
1321 | 1325 | |
|
1322 | 1326 | def get_results(self, client_id, msg): |
|
1323 | 1327 | """Get the result of 1 or more messages.""" |
|
1324 | 1328 | content = msg['content'] |
|
1325 | 1329 | msg_ids = sorted(set(content['msg_ids'])) |
|
1326 | 1330 | statusonly = content.get('status_only', False) |
|
1327 | 1331 | pending = [] |
|
1328 | 1332 | completed = [] |
|
1329 | 1333 | content = dict(status='ok') |
|
1330 | 1334 | content['pending'] = pending |
|
1331 | 1335 | content['completed'] = completed |
|
1332 | 1336 | buffers = [] |
|
1333 | 1337 | if not statusonly: |
|
1334 | 1338 | try: |
|
1335 | 1339 | matches = self.db.find_records(dict(msg_id={'$in':msg_ids})) |
|
1336 | 1340 | # turn match list into dict, for faster lookup |
|
1337 | 1341 | records = {} |
|
1338 | 1342 | for rec in matches: |
|
1339 | 1343 | records[rec['msg_id']] = rec |
|
1340 | 1344 | except Exception: |
|
1341 | 1345 | content = error.wrap_exception() |
|
1342 | 1346 | self.session.send(self.query, "result_reply", content=content, |
|
1343 | 1347 | parent=msg, ident=client_id) |
|
1344 | 1348 | return |
|
1345 | 1349 | else: |
|
1346 | 1350 | records = {} |
|
1347 | 1351 | for msg_id in msg_ids: |
|
1348 | 1352 | if msg_id in self.pending: |
|
1349 | 1353 | pending.append(msg_id) |
|
1350 | 1354 | elif msg_id in self.all_completed: |
|
1351 | 1355 | completed.append(msg_id) |
|
1352 | 1356 | if not statusonly: |
|
1353 | 1357 | c,bufs = self._extract_record(records[msg_id]) |
|
1354 | 1358 | content[msg_id] = c |
|
1355 | 1359 | buffers.extend(bufs) |
|
1356 | 1360 | elif msg_id in records: |
|
1357 | 1361 | if rec['completed']: |
|
1358 | 1362 | completed.append(msg_id) |
|
1359 | 1363 | c,bufs = self._extract_record(records[msg_id]) |
|
1360 | 1364 | content[msg_id] = c |
|
1361 | 1365 | buffers.extend(bufs) |
|
1362 | 1366 | else: |
|
1363 | 1367 | pending.append(msg_id) |
|
1364 | 1368 | else: |
|
1365 | 1369 | try: |
|
1366 | 1370 | raise KeyError('No such message: '+msg_id) |
|
1367 | 1371 | except: |
|
1368 | 1372 | content = error.wrap_exception() |
|
1369 | 1373 | break |
|
1370 | 1374 | self.session.send(self.query, "result_reply", content=content, |
|
1371 | 1375 | parent=msg, ident=client_id, |
|
1372 | 1376 | buffers=buffers) |
|
1373 | 1377 | |
|
1374 | 1378 | def get_history(self, client_id, msg): |
|
1375 | 1379 | """Get a list of all msg_ids in our DB records""" |
|
1376 | 1380 | try: |
|
1377 | 1381 | msg_ids = self.db.get_history() |
|
1378 | 1382 | except Exception as e: |
|
1379 | 1383 | content = error.wrap_exception() |
|
1380 | 1384 | else: |
|
1381 | 1385 | content = dict(status='ok', history=msg_ids) |
|
1382 | 1386 | |
|
1383 | 1387 | self.session.send(self.query, "history_reply", content=content, |
|
1384 | 1388 | parent=msg, ident=client_id) |
|
1385 | 1389 | |
|
1386 | 1390 | def db_query(self, client_id, msg): |
|
1387 | 1391 | """Perform a raw query on the task record database.""" |
|
1388 | 1392 | content = msg['content'] |
|
1389 | 1393 | query = extract_dates(content.get('query', {})) |
|
1390 | 1394 | keys = content.get('keys', None) |
|
1391 | 1395 | buffers = [] |
|
1392 | 1396 | empty = list() |
|
1393 | 1397 | try: |
|
1394 | 1398 | records = self.db.find_records(query, keys) |
|
1395 | 1399 | except Exception as e: |
|
1396 | 1400 | content = error.wrap_exception() |
|
1397 | 1401 | else: |
|
1398 | 1402 | # extract buffers from reply content: |
|
1399 | 1403 | if keys is not None: |
|
1400 | 1404 | buffer_lens = [] if 'buffers' in keys else None |
|
1401 | 1405 | result_buffer_lens = [] if 'result_buffers' in keys else None |
|
1402 | 1406 | else: |
|
1403 | 1407 | buffer_lens = None |
|
1404 | 1408 | result_buffer_lens = None |
|
1405 | 1409 | |
|
1406 | 1410 | for rec in records: |
|
1407 | 1411 | # buffers may be None, so double check |
|
1408 | 1412 | b = rec.pop('buffers', empty) or empty |
|
1409 | 1413 | if buffer_lens is not None: |
|
1410 | 1414 | buffer_lens.append(len(b)) |
|
1411 | 1415 | buffers.extend(b) |
|
1412 | 1416 | rb = rec.pop('result_buffers', empty) or empty |
|
1413 | 1417 | if result_buffer_lens is not None: |
|
1414 | 1418 | result_buffer_lens.append(len(rb)) |
|
1415 | 1419 | buffers.extend(rb) |
|
1416 | 1420 | content = dict(status='ok', records=records, buffer_lens=buffer_lens, |
|
1417 | 1421 | result_buffer_lens=result_buffer_lens) |
|
1418 | 1422 | # self.log.debug (content) |
|
1419 | 1423 | self.session.send(self.query, "db_reply", content=content, |
|
1420 | 1424 | parent=msg, ident=client_id, |
|
1421 | 1425 | buffers=buffers) |
|
1422 | 1426 |
@@ -1,369 +1,370 b'' | |||
|
1 | 1 | """some generic utilities for dealing with classes, urls, and serialization |
|
2 | 2 | |
|
3 | 3 | Authors: |
|
4 | 4 | |
|
5 | 5 | * Min RK |
|
6 | 6 | """ |
|
7 | 7 | #----------------------------------------------------------------------------- |
|
8 | 8 | # Copyright (C) 2010-2011 The IPython Development Team |
|
9 | 9 | # |
|
10 | 10 | # Distributed under the terms of the BSD License. The full license is in |
|
11 | 11 | # the file COPYING, distributed as part of this software. |
|
12 | 12 | #----------------------------------------------------------------------------- |
|
13 | 13 | |
|
14 | 14 | #----------------------------------------------------------------------------- |
|
15 | 15 | # Imports |
|
16 | 16 | #----------------------------------------------------------------------------- |
|
17 | 17 | |
|
18 | 18 | # Standard library imports. |
|
19 | 19 | import logging |
|
20 | 20 | import os |
|
21 | 21 | import re |
|
22 | 22 | import stat |
|
23 | 23 | import socket |
|
24 | 24 | import sys |
|
25 | 25 | from signal import signal, SIGINT, SIGABRT, SIGTERM |
|
26 | 26 | try: |
|
27 | 27 | from signal import SIGKILL |
|
28 | 28 | except ImportError: |
|
29 | 29 | SIGKILL=None |
|
30 | 30 | |
|
31 | 31 | try: |
|
32 | 32 | import cPickle |
|
33 | 33 | pickle = cPickle |
|
34 | 34 | except: |
|
35 | 35 | cPickle = None |
|
36 | 36 | import pickle |
|
37 | 37 | |
|
38 | 38 | # System library imports |
|
39 | 39 | import zmq |
|
40 | 40 | from zmq.log import handlers |
|
41 | 41 | |
|
42 | 42 | from IPython.external.decorator import decorator |
|
43 | 43 | |
|
44 | 44 | # IPython imports |
|
45 | 45 | from IPython.config.application import Application |
|
46 | 46 | from IPython.utils.localinterfaces import localhost, is_public_ip, public_ips |
|
47 | 47 | from IPython.utils.py3compat import string_types, iteritems, itervalues |
|
48 | 48 | from IPython.kernel.zmq.log import EnginePUBHandler |
|
49 | 49 | from IPython.kernel.zmq.serialize import ( |
|
50 | 50 | unserialize_object, serialize_object, pack_apply_message, unpack_apply_message |
|
51 | 51 | ) |
|
52 | 52 | |
|
53 | 53 | #----------------------------------------------------------------------------- |
|
54 | 54 | # Classes |
|
55 | 55 | #----------------------------------------------------------------------------- |
|
56 | 56 | |
|
57 | 57 | class Namespace(dict): |
|
58 | 58 | """Subclass of dict for attribute access to keys.""" |
|
59 | 59 | |
|
60 | 60 | def __getattr__(self, key): |
|
61 | 61 | """getattr aliased to getitem""" |
|
62 | 62 | if key in self: |
|
63 | 63 | return self[key] |
|
64 | 64 | else: |
|
65 | 65 | raise NameError(key) |
|
66 | 66 | |
|
67 | 67 | def __setattr__(self, key, value): |
|
68 | 68 | """setattr aliased to setitem, with strict""" |
|
69 | 69 | if hasattr(dict, key): |
|
70 | 70 | raise KeyError("Cannot override dict keys %r"%key) |
|
71 | 71 | self[key] = value |
|
72 | 72 | |
|
73 | 73 | |
|
74 | 74 | class ReverseDict(dict): |
|
75 | 75 | """simple double-keyed subset of dict methods.""" |
|
76 | 76 | |
|
77 | 77 | def __init__(self, *args, **kwargs): |
|
78 | 78 | dict.__init__(self, *args, **kwargs) |
|
79 | 79 | self._reverse = dict() |
|
80 | 80 | for key, value in iteritems(self): |
|
81 | 81 | self._reverse[value] = key |
|
82 | 82 | |
|
83 | 83 | def __getitem__(self, key): |
|
84 | 84 | try: |
|
85 | 85 | return dict.__getitem__(self, key) |
|
86 | 86 | except KeyError: |
|
87 | 87 | return self._reverse[key] |
|
88 | 88 | |
|
89 | 89 | def __setitem__(self, key, value): |
|
90 | 90 | if key in self._reverse: |
|
91 | 91 | raise KeyError("Can't have key %r on both sides!"%key) |
|
92 | 92 | dict.__setitem__(self, key, value) |
|
93 | 93 | self._reverse[value] = key |
|
94 | 94 | |
|
95 | 95 | def pop(self, key): |
|
96 | 96 | value = dict.pop(self, key) |
|
97 | 97 | self._reverse.pop(value) |
|
98 | 98 | return value |
|
99 | 99 | |
|
100 | 100 | def get(self, key, default=None): |
|
101 | 101 | try: |
|
102 | 102 | return self[key] |
|
103 | 103 | except KeyError: |
|
104 | 104 | return default |
|
105 | 105 | |
|
106 | 106 | #----------------------------------------------------------------------------- |
|
107 | 107 | # Functions |
|
108 | 108 | #----------------------------------------------------------------------------- |
|
109 | 109 | |
|
110 | 110 | @decorator |
|
111 | 111 | def log_errors(f, self, *args, **kwargs): |
|
112 | 112 | """decorator to log unhandled exceptions raised in a method. |
|
113 | 113 | |
|
114 | 114 | For use wrapping on_recv callbacks, so that exceptions |
|
115 | 115 | do not cause the stream to be closed. |
|
116 | 116 | """ |
|
117 | 117 | try: |
|
118 | 118 | return f(self, *args, **kwargs) |
|
119 | 119 | except Exception: |
|
120 | 120 | self.log.error("Uncaught exception in %r" % f, exc_info=True) |
|
121 | 121 | |
|
122 | 122 | |
|
123 | 123 | def is_url(url): |
|
124 | 124 | """boolean check for whether a string is a zmq url""" |
|
125 | 125 | if '://' not in url: |
|
126 | 126 | return False |
|
127 | 127 | proto, addr = url.split('://', 1) |
|
128 | 128 | if proto.lower() not in ['tcp','pgm','epgm','ipc','inproc']: |
|
129 | 129 | return False |
|
130 | 130 | return True |
|
131 | 131 | |
|
132 | 132 | def validate_url(url): |
|
133 | 133 | """validate a url for zeromq""" |
|
134 | 134 | if not isinstance(url, string_types): |
|
135 | 135 | raise TypeError("url must be a string, not %r"%type(url)) |
|
136 | 136 | url = url.lower() |
|
137 | 137 | |
|
138 | 138 | proto_addr = url.split('://') |
|
139 | 139 | assert len(proto_addr) == 2, 'Invalid url: %r'%url |
|
140 | 140 | proto, addr = proto_addr |
|
141 | 141 | assert proto in ['tcp','pgm','epgm','ipc','inproc'], "Invalid protocol: %r"%proto |
|
142 | 142 | |
|
143 | 143 | # domain pattern adapted from http://www.regexlib.com/REDetails.aspx?regexp_id=391 |
|
144 | 144 | # author: Remi Sabourin |
|
145 | 145 | pat = re.compile(r'^([\w\d]([\w\d\-]{0,61}[\w\d])?\.)*[\w\d]([\w\d\-]{0,61}[\w\d])?$') |
|
146 | 146 | |
|
147 | 147 | if proto == 'tcp': |
|
148 | 148 | lis = addr.split(':') |
|
149 | 149 | assert len(lis) == 2, 'Invalid url: %r'%url |
|
150 | 150 | addr,s_port = lis |
|
151 | 151 | try: |
|
152 | 152 | port = int(s_port) |
|
153 | 153 | except ValueError: |
|
154 | 154 | raise AssertionError("Invalid port %r in url: %r"%(port, url)) |
|
155 | 155 | |
|
156 | 156 | assert addr == '*' or pat.match(addr) is not None, 'Invalid url: %r'%url |
|
157 | 157 | |
|
158 | 158 | else: |
|
159 | 159 | # only validate tcp urls currently |
|
160 | 160 | pass |
|
161 | 161 | |
|
162 | 162 | return True |
|
163 | 163 | |
|
164 | 164 | |
|
165 | 165 | def validate_url_container(container): |
|
166 | 166 | """validate a potentially nested collection of urls.""" |
|
167 | 167 | if isinstance(container, string_types): |
|
168 | 168 | url = container |
|
169 | 169 | return validate_url(url) |
|
170 | 170 | elif isinstance(container, dict): |
|
171 | 171 | container = itervalues(container) |
|
172 | 172 | |
|
173 | 173 | for element in container: |
|
174 | 174 | validate_url_container(element) |
|
175 | 175 | |
|
176 | 176 | |
|
177 | 177 | def split_url(url): |
|
178 | 178 | """split a zmq url (tcp://ip:port) into ('tcp','ip','port').""" |
|
179 | 179 | proto_addr = url.split('://') |
|
180 | 180 | assert len(proto_addr) == 2, 'Invalid url: %r'%url |
|
181 | 181 | proto, addr = proto_addr |
|
182 | 182 | lis = addr.split(':') |
|
183 | 183 | assert len(lis) == 2, 'Invalid url: %r'%url |
|
184 | 184 | addr,s_port = lis |
|
185 | 185 | return proto,addr,s_port |
|
186 | 186 | |
|
187 | 187 | def disambiguate_ip_address(ip, location=None): |
|
188 | 188 | """turn multi-ip interfaces '0.0.0.0' and '*' into connectable |
|
189 | 189 | ones, based on the location (default interpretation of location is localhost).""" |
|
190 | 190 | if ip in ('0.0.0.0', '*'): |
|
191 | 191 | if location is None or is_public_ip(location) or not public_ips(): |
|
192 | 192 | # If location is unspecified or cannot be determined, assume local |
|
193 | 193 | ip = localhost() |
|
194 | 194 | elif location: |
|
195 | 195 | return location |
|
196 | 196 | return ip |
|
197 | 197 | |
|
198 | 198 | def disambiguate_url(url, location=None): |
|
199 | 199 | """turn multi-ip interfaces '0.0.0.0' and '*' into connectable |
|
200 | 200 | ones, based on the location (default interpretation is localhost). |
|
201 | 201 | |
|
202 |
This is for zeromq urls, such as tcp://*:10101. |
|
|
202 | This is for zeromq urls, such as ``tcp://*:10101``. | |
|
203 | """ | |
|
203 | 204 | try: |
|
204 | 205 | proto,ip,port = split_url(url) |
|
205 | 206 | except AssertionError: |
|
206 | 207 | # probably not tcp url; could be ipc, etc. |
|
207 | 208 | return url |
|
208 | 209 | |
|
209 | 210 | ip = disambiguate_ip_address(ip,location) |
|
210 | 211 | |
|
211 | 212 | return "%s://%s:%s"%(proto,ip,port) |
|
212 | 213 | |
|
213 | 214 | |
|
214 | 215 | #-------------------------------------------------------------------------- |
|
215 | 216 | # helpers for implementing old MEC API via view.apply |
|
216 | 217 | #-------------------------------------------------------------------------- |
|
217 | 218 | |
|
218 | 219 | def interactive(f): |
|
219 | 220 | """decorator for making functions appear as interactively defined. |
|
220 | 221 | This results in the function being linked to the user_ns as globals() |
|
221 | 222 | instead of the module globals(). |
|
222 | 223 | """ |
|
223 | 224 | f.__module__ = '__main__' |
|
224 | 225 | return f |
|
225 | 226 | |
|
226 | 227 | @interactive |
|
227 | 228 | def _push(**ns): |
|
228 | 229 | """helper method for implementing `client.push` via `client.apply`""" |
|
229 | 230 | user_ns = globals() |
|
230 | 231 | tmp = '_IP_PUSH_TMP_' |
|
231 | 232 | while tmp in user_ns: |
|
232 | 233 | tmp = tmp + '_' |
|
233 | 234 | try: |
|
234 | 235 | for name, value in ns.items(): |
|
235 | 236 | user_ns[tmp] = value |
|
236 | 237 | exec("%s = %s" % (name, tmp), user_ns) |
|
237 | 238 | finally: |
|
238 | 239 | user_ns.pop(tmp, None) |
|
239 | 240 | |
|
240 | 241 | @interactive |
|
241 | 242 | def _pull(keys): |
|
242 | 243 | """helper method for implementing `client.pull` via `client.apply`""" |
|
243 | 244 | if isinstance(keys, (list,tuple, set)): |
|
244 | 245 | return [eval(key, globals()) for key in keys] |
|
245 | 246 | else: |
|
246 | 247 | return eval(keys, globals()) |
|
247 | 248 | |
|
248 | 249 | @interactive |
|
249 | 250 | def _execute(code): |
|
250 | 251 | """helper method for implementing `client.execute` via `client.apply`""" |
|
251 | 252 | exec(code, globals()) |
|
252 | 253 | |
|
253 | 254 | #-------------------------------------------------------------------------- |
|
254 | 255 | # extra process management utilities |
|
255 | 256 | #-------------------------------------------------------------------------- |
|
256 | 257 | |
|
257 | 258 | _random_ports = set() |
|
258 | 259 | |
|
259 | 260 | def select_random_ports(n): |
|
260 | 261 | """Selects and return n random ports that are available.""" |
|
261 | 262 | ports = [] |
|
262 | 263 | for i in range(n): |
|
263 | 264 | sock = socket.socket() |
|
264 | 265 | sock.bind(('', 0)) |
|
265 | 266 | while sock.getsockname()[1] in _random_ports: |
|
266 | 267 | sock.close() |
|
267 | 268 | sock = socket.socket() |
|
268 | 269 | sock.bind(('', 0)) |
|
269 | 270 | ports.append(sock) |
|
270 | 271 | for i, sock in enumerate(ports): |
|
271 | 272 | port = sock.getsockname()[1] |
|
272 | 273 | sock.close() |
|
273 | 274 | ports[i] = port |
|
274 | 275 | _random_ports.add(port) |
|
275 | 276 | return ports |
|
276 | 277 | |
|
277 | 278 | def signal_children(children): |
|
278 | 279 | """Relay interupt/term signals to children, for more solid process cleanup.""" |
|
279 | 280 | def terminate_children(sig, frame): |
|
280 | 281 | log = Application.instance().log |
|
281 | 282 | log.critical("Got signal %i, terminating children..."%sig) |
|
282 | 283 | for child in children: |
|
283 | 284 | child.terminate() |
|
284 | 285 | |
|
285 | 286 | sys.exit(sig != SIGINT) |
|
286 | 287 | # sys.exit(sig) |
|
287 | 288 | for sig in (SIGINT, SIGABRT, SIGTERM): |
|
288 | 289 | signal(sig, terminate_children) |
|
289 | 290 | |
|
290 | 291 | def generate_exec_key(keyfile): |
|
291 | 292 | import uuid |
|
292 | 293 | newkey = str(uuid.uuid4()) |
|
293 | 294 | with open(keyfile, 'w') as f: |
|
294 | 295 | # f.write('ipython-key ') |
|
295 | 296 | f.write(newkey+'\n') |
|
296 | 297 | # set user-only RW permissions (0600) |
|
297 | 298 | # this will have no effect on Windows |
|
298 | 299 | os.chmod(keyfile, stat.S_IRUSR|stat.S_IWUSR) |
|
299 | 300 | |
|
300 | 301 | |
|
301 | 302 | def integer_loglevel(loglevel): |
|
302 | 303 | try: |
|
303 | 304 | loglevel = int(loglevel) |
|
304 | 305 | except ValueError: |
|
305 | 306 | if isinstance(loglevel, str): |
|
306 | 307 | loglevel = getattr(logging, loglevel) |
|
307 | 308 | return loglevel |
|
308 | 309 | |
|
309 | 310 | def connect_logger(logname, context, iface, root="ip", loglevel=logging.DEBUG): |
|
310 | 311 | logger = logging.getLogger(logname) |
|
311 | 312 | if any([isinstance(h, handlers.PUBHandler) for h in logger.handlers]): |
|
312 | 313 | # don't add a second PUBHandler |
|
313 | 314 | return |
|
314 | 315 | loglevel = integer_loglevel(loglevel) |
|
315 | 316 | lsock = context.socket(zmq.PUB) |
|
316 | 317 | lsock.connect(iface) |
|
317 | 318 | handler = handlers.PUBHandler(lsock) |
|
318 | 319 | handler.setLevel(loglevel) |
|
319 | 320 | handler.root_topic = root |
|
320 | 321 | logger.addHandler(handler) |
|
321 | 322 | logger.setLevel(loglevel) |
|
322 | 323 | |
|
323 | 324 | def connect_engine_logger(context, iface, engine, loglevel=logging.DEBUG): |
|
324 | 325 | logger = logging.getLogger() |
|
325 | 326 | if any([isinstance(h, handlers.PUBHandler) for h in logger.handlers]): |
|
326 | 327 | # don't add a second PUBHandler |
|
327 | 328 | return |
|
328 | 329 | loglevel = integer_loglevel(loglevel) |
|
329 | 330 | lsock = context.socket(zmq.PUB) |
|
330 | 331 | lsock.connect(iface) |
|
331 | 332 | handler = EnginePUBHandler(engine, lsock) |
|
332 | 333 | handler.setLevel(loglevel) |
|
333 | 334 | logger.addHandler(handler) |
|
334 | 335 | logger.setLevel(loglevel) |
|
335 | 336 | return logger |
|
336 | 337 | |
|
337 | 338 | def local_logger(logname, loglevel=logging.DEBUG): |
|
338 | 339 | loglevel = integer_loglevel(loglevel) |
|
339 | 340 | logger = logging.getLogger(logname) |
|
340 | 341 | if any([isinstance(h, logging.StreamHandler) for h in logger.handlers]): |
|
341 | 342 | # don't add a second StreamHandler |
|
342 | 343 | return |
|
343 | 344 | handler = logging.StreamHandler() |
|
344 | 345 | handler.setLevel(loglevel) |
|
345 | 346 | formatter = logging.Formatter("%(asctime)s.%(msecs).03d [%(name)s] %(message)s", |
|
346 | 347 | datefmt="%Y-%m-%d %H:%M:%S") |
|
347 | 348 | handler.setFormatter(formatter) |
|
348 | 349 | |
|
349 | 350 | logger.addHandler(handler) |
|
350 | 351 | logger.setLevel(loglevel) |
|
351 | 352 | return logger |
|
352 | 353 | |
|
353 | 354 | def set_hwm(sock, hwm=0): |
|
354 | 355 | """set zmq High Water Mark on a socket |
|
355 | 356 | |
|
356 | 357 | in a way that always works for various pyzmq / libzmq versions. |
|
357 | 358 | """ |
|
358 | 359 | import zmq |
|
359 | 360 | |
|
360 | 361 | for key in ('HWM', 'SNDHWM', 'RCVHWM'): |
|
361 | 362 | opt = getattr(zmq, key, None) |
|
362 | 363 | if opt is None: |
|
363 | 364 | continue |
|
364 | 365 | try: |
|
365 | 366 | sock.setsockopt(opt, hwm) |
|
366 | 367 | except zmq.ZMQError: |
|
367 | 368 | pass |
|
368 | 369 | |
|
369 | 370 | No newline at end of file |
@@ -1,692 +1,691 b'' | |||
|
1 | 1 | # -*- coding: utf-8 -*- |
|
2 | 2 | """Subclass of InteractiveShell for terminal based frontends.""" |
|
3 | 3 | |
|
4 | 4 | #----------------------------------------------------------------------------- |
|
5 | 5 | # Copyright (C) 2001 Janko Hauser <jhauser@zscout.de> |
|
6 | 6 | # Copyright (C) 2001-2007 Fernando Perez. <fperez@colorado.edu> |
|
7 | 7 | # Copyright (C) 2008-2011 The IPython Development Team |
|
8 | 8 | # |
|
9 | 9 | # Distributed under the terms of the BSD License. The full license is in |
|
10 | 10 | # the file COPYING, distributed as part of this software. |
|
11 | 11 | #----------------------------------------------------------------------------- |
|
12 | 12 | |
|
13 | 13 | #----------------------------------------------------------------------------- |
|
14 | 14 | # Imports |
|
15 | 15 | #----------------------------------------------------------------------------- |
|
16 | 16 | from __future__ import print_function |
|
17 | 17 | |
|
18 | 18 | import bdb |
|
19 | 19 | import os |
|
20 | 20 | import sys |
|
21 | 21 | |
|
22 | 22 | from IPython.core.error import TryNext, UsageError |
|
23 | 23 | from IPython.core.usage import interactive_usage, default_banner |
|
24 | 24 | from IPython.core.inputsplitter import IPythonInputSplitter |
|
25 | 25 | from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC |
|
26 | 26 | from IPython.core.magic import Magics, magics_class, line_magic |
|
27 | 27 | from IPython.lib.clipboard import ClipboardEmpty |
|
28 | 28 | from IPython.testing.skipdoctest import skip_doctest |
|
29 | 29 | from IPython.utils.encoding import get_stream_enc |
|
30 | 30 | from IPython.utils import py3compat |
|
31 | 31 | from IPython.utils.terminal import toggle_set_term_title, set_term_title |
|
32 | 32 | from IPython.utils.process import abbrev_cwd |
|
33 | 33 | from IPython.utils.warn import warn, error |
|
34 | 34 | from IPython.utils.text import num_ini_spaces, SList, strip_email_quotes |
|
35 | 35 | from IPython.utils.traitlets import Integer, CBool, Unicode |
|
36 | 36 | |
|
37 | 37 | #----------------------------------------------------------------------------- |
|
38 | 38 | # Utilities |
|
39 | 39 | #----------------------------------------------------------------------------- |
|
40 | 40 | |
|
41 | 41 | def get_default_editor(): |
|
42 | 42 | try: |
|
43 | 43 | ed = os.environ['EDITOR'] |
|
44 | 44 | except KeyError: |
|
45 | 45 | if os.name == 'posix': |
|
46 | 46 | ed = 'vi' # the only one guaranteed to be there! |
|
47 | 47 | else: |
|
48 | 48 | ed = 'notepad' # same in Windows! |
|
49 | 49 | return ed |
|
50 | 50 | |
|
51 | 51 | |
|
52 | 52 | def get_pasted_lines(sentinel, l_input=py3compat.input): |
|
53 | 53 | """ Yield pasted lines until the user enters the given sentinel value. |
|
54 | 54 | """ |
|
55 | 55 | print("Pasting code; enter '%s' alone on the line to stop or use Ctrl-D." \ |
|
56 | 56 | % sentinel) |
|
57 | 57 | while True: |
|
58 | 58 | try: |
|
59 | 59 | l = l_input(':') |
|
60 | 60 | if l == sentinel: |
|
61 | 61 | return |
|
62 | 62 | else: |
|
63 | 63 | yield l |
|
64 | 64 | except EOFError: |
|
65 | 65 | print('<EOF>') |
|
66 | 66 | return |
|
67 | 67 | |
|
68 | 68 | |
|
69 | 69 | #------------------------------------------------------------------------ |
|
70 | 70 | # Terminal-specific magics |
|
71 | 71 | #------------------------------------------------------------------------ |
|
72 | 72 | |
|
73 | 73 | @magics_class |
|
74 | 74 | class TerminalMagics(Magics): |
|
75 | 75 | def __init__(self, shell): |
|
76 | 76 | super(TerminalMagics, self).__init__(shell) |
|
77 | 77 | self.input_splitter = IPythonInputSplitter() |
|
78 | 78 | |
|
79 | 79 | def store_or_execute(self, block, name): |
|
80 | 80 | """ Execute a block, or store it in a variable, per the user's request. |
|
81 | 81 | """ |
|
82 | 82 | if name: |
|
83 | 83 | # If storing it for further editing |
|
84 | 84 | self.shell.user_ns[name] = SList(block.splitlines()) |
|
85 | 85 | print("Block assigned to '%s'" % name) |
|
86 | 86 | else: |
|
87 | 87 | b = self.preclean_input(block) |
|
88 | 88 | self.shell.user_ns['pasted_block'] = b |
|
89 | 89 | self.shell.using_paste_magics = True |
|
90 | 90 | try: |
|
91 | 91 | self.shell.run_cell(b) |
|
92 | 92 | finally: |
|
93 | 93 | self.shell.using_paste_magics = False |
|
94 | 94 | |
|
95 | 95 | def preclean_input(self, block): |
|
96 | 96 | lines = block.splitlines() |
|
97 | 97 | while lines and not lines[0].strip(): |
|
98 | 98 | lines = lines[1:] |
|
99 | 99 | return strip_email_quotes('\n'.join(lines)) |
|
100 | 100 | |
|
101 | 101 | def rerun_pasted(self, name='pasted_block'): |
|
102 | 102 | """ Rerun a previously pasted command. |
|
103 | 103 | """ |
|
104 | 104 | b = self.shell.user_ns.get(name) |
|
105 | 105 | |
|
106 | 106 | # Sanity checks |
|
107 | 107 | if b is None: |
|
108 | 108 | raise UsageError('No previous pasted block available') |
|
109 | 109 | if not isinstance(b, py3compat.string_types): |
|
110 | 110 | raise UsageError( |
|
111 | 111 | "Variable 'pasted_block' is not a string, can't execute") |
|
112 | 112 | |
|
113 | 113 | print("Re-executing '%s...' (%d chars)"% (b.split('\n',1)[0], len(b))) |
|
114 | 114 | self.shell.run_cell(b) |
|
115 | 115 | |
|
116 | 116 | @line_magic |
|
117 | 117 | def autoindent(self, parameter_s = ''): |
|
118 | 118 | """Toggle autoindent on/off (if available).""" |
|
119 | 119 | |
|
120 | 120 | self.shell.set_autoindent() |
|
121 | 121 | print("Automatic indentation is:",['OFF','ON'][self.shell.autoindent]) |
|
122 | 122 | |
|
123 | 123 | @skip_doctest |
|
124 | 124 | @line_magic |
|
125 | 125 | def cpaste(self, parameter_s=''): |
|
126 | 126 | """Paste & execute a pre-formatted code block from clipboard. |
|
127 | 127 | |
|
128 | 128 | You must terminate the block with '--' (two minus-signs) or Ctrl-D |
|
129 | 129 | alone on the line. You can also provide your own sentinel with '%paste |
|
130 | 130 | -s %%' ('%%' is the new sentinel for this operation) |
|
131 | 131 | |
|
132 | 132 | The block is dedented prior to execution to enable execution of method |
|
133 | 133 | definitions. '>' and '+' characters at the beginning of a line are |
|
134 | 134 | ignored, to allow pasting directly from e-mails, diff files and |
|
135 | 135 | doctests (the '...' continuation prompt is also stripped). The |
|
136 | 136 | executed block is also assigned to variable named 'pasted_block' for |
|
137 | 137 | later editing with '%edit pasted_block'. |
|
138 | 138 | |
|
139 | 139 | You can also pass a variable name as an argument, e.g. '%cpaste foo'. |
|
140 | 140 | This assigns the pasted block to variable 'foo' as string, without |
|
141 | 141 | dedenting or executing it (preceding >>> and + is still stripped) |
|
142 | 142 | |
|
143 | 143 | '%cpaste -r' re-executes the block previously entered by cpaste. |
|
144 | 144 | |
|
145 | 145 | Do not be alarmed by garbled output on Windows (it's a readline bug). |
|
146 | 146 | Just press enter and type -- (and press enter again) and the block |
|
147 | 147 | will be what was just pasted. |
|
148 | 148 | |
|
149 | 149 | IPython statements (magics, shell escapes) are not supported (yet). |
|
150 | 150 | |
|
151 | 151 | See also |
|
152 | 152 | -------- |
|
153 | 153 | paste: automatically pull code from clipboard. |
|
154 | 154 | |
|
155 | 155 | Examples |
|
156 | 156 | -------- |
|
157 | 157 | :: |
|
158 | 158 | |
|
159 | 159 | In [8]: %cpaste |
|
160 | 160 | Pasting code; enter '--' alone on the line to stop. |
|
161 | 161 | :>>> a = ["world!", "Hello"] |
|
162 | 162 | :>>> print " ".join(sorted(a)) |
|
163 | 163 | :-- |
|
164 | 164 | Hello world! |
|
165 | 165 | """ |
|
166 | 166 | opts, name = self.parse_options(parameter_s, 'rs:', mode='string') |
|
167 | 167 | if 'r' in opts: |
|
168 | 168 | self.rerun_pasted() |
|
169 | 169 | return |
|
170 | 170 | |
|
171 | 171 | sentinel = opts.get('s', '--') |
|
172 | 172 | block = '\n'.join(get_pasted_lines(sentinel)) |
|
173 | 173 | self.store_or_execute(block, name) |
|
174 | 174 | |
|
175 | 175 | @line_magic |
|
176 | 176 | def paste(self, parameter_s=''): |
|
177 | 177 | """Paste & execute a pre-formatted code block from clipboard. |
|
178 | 178 | |
|
179 | 179 | The text is pulled directly from the clipboard without user |
|
180 | 180 | intervention and printed back on the screen before execution (unless |
|
181 | 181 | the -q flag is given to force quiet mode). |
|
182 | 182 | |
|
183 | 183 | The block is dedented prior to execution to enable execution of method |
|
184 | 184 | definitions. '>' and '+' characters at the beginning of a line are |
|
185 | 185 | ignored, to allow pasting directly from e-mails, diff files and |
|
186 | 186 | doctests (the '...' continuation prompt is also stripped). The |
|
187 | 187 | executed block is also assigned to variable named 'pasted_block' for |
|
188 | 188 | later editing with '%edit pasted_block'. |
|
189 | 189 | |
|
190 | 190 | You can also pass a variable name as an argument, e.g. '%paste foo'. |
|
191 | 191 | This assigns the pasted block to variable 'foo' as string, without |
|
192 | 192 | executing it (preceding >>> and + is still stripped). |
|
193 | 193 | |
|
194 | 194 | Options: |
|
195 | 195 | |
|
196 | 196 | -r: re-executes the block previously entered by cpaste. |
|
197 | 197 | |
|
198 | 198 | -q: quiet mode: do not echo the pasted text back to the terminal. |
|
199 | 199 | |
|
200 | 200 | IPython statements (magics, shell escapes) are not supported (yet). |
|
201 | 201 | |
|
202 | 202 | See also |
|
203 | 203 | -------- |
|
204 | 204 | cpaste: manually paste code into terminal until you mark its end. |
|
205 | 205 | """ |
|
206 | 206 | opts, name = self.parse_options(parameter_s, 'rq', mode='string') |
|
207 | 207 | if 'r' in opts: |
|
208 | 208 | self.rerun_pasted() |
|
209 | 209 | return |
|
210 | 210 | try: |
|
211 | 211 | block = self.shell.hooks.clipboard_get() |
|
212 | 212 | except TryNext as clipboard_exc: |
|
213 | 213 | message = getattr(clipboard_exc, 'args') |
|
214 | 214 | if message: |
|
215 | 215 | error(message[0]) |
|
216 | 216 | else: |
|
217 | 217 | error('Could not get text from the clipboard.') |
|
218 | 218 | return |
|
219 | 219 | except ClipboardEmpty: |
|
220 | 220 | raise UsageError("The clipboard appears to be empty") |
|
221 | 221 | |
|
222 | 222 | # By default, echo back to terminal unless quiet mode is requested |
|
223 | 223 | if 'q' not in opts: |
|
224 | 224 | write = self.shell.write |
|
225 | 225 | write(self.shell.pycolorize(block)) |
|
226 | 226 | if not block.endswith('\n'): |
|
227 | 227 | write('\n') |
|
228 | 228 | write("## -- End pasted text --\n") |
|
229 | 229 | |
|
230 | 230 | self.store_or_execute(block, name) |
|
231 | 231 | |
|
232 | 232 | # Class-level: add a '%cls' magic only on Windows |
|
233 | 233 | if sys.platform == 'win32': |
|
234 | 234 | @line_magic |
|
235 | 235 | def cls(self, s): |
|
236 | 236 | """Clear screen. |
|
237 | 237 | """ |
|
238 | 238 | os.system("cls") |
|
239 | 239 | |
|
240 | 240 | #----------------------------------------------------------------------------- |
|
241 | 241 | # Main class |
|
242 | 242 | #----------------------------------------------------------------------------- |
|
243 | 243 | |
|
244 | 244 | class TerminalInteractiveShell(InteractiveShell): |
|
245 | 245 | |
|
246 | 246 | autoedit_syntax = CBool(False, config=True, |
|
247 | 247 | help="auto editing of files with syntax errors.") |
|
248 | 248 | banner = Unicode('') |
|
249 | 249 | banner1 = Unicode(default_banner, config=True, |
|
250 | 250 | help="""The part of the banner to be printed before the profile""" |
|
251 | 251 | ) |
|
252 | 252 | banner2 = Unicode('', config=True, |
|
253 | 253 | help="""The part of the banner to be printed after the profile""" |
|
254 | 254 | ) |
|
255 | 255 | confirm_exit = CBool(True, config=True, |
|
256 | 256 | help=""" |
|
257 | 257 | Set to confirm when you try to exit IPython with an EOF (Control-D |
|
258 | 258 | in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit', |
|
259 | 259 | you can force a direct exit without any confirmation.""", |
|
260 | 260 | ) |
|
261 | 261 | # This display_banner only controls whether or not self.show_banner() |
|
262 | 262 | # is called when mainloop/interact are called. The default is False |
|
263 | 263 | # because for the terminal based application, the banner behavior |
|
264 | 264 | # is controlled by Global.display_banner, which IPythonApp looks at |
|
265 | 265 | # to determine if *it* should call show_banner() by hand or not. |
|
266 | 266 | display_banner = CBool(False) # This isn't configurable! |
|
267 | 267 | embedded = CBool(False) |
|
268 | 268 | embedded_active = CBool(False) |
|
269 | 269 | editor = Unicode(get_default_editor(), config=True, |
|
270 | 270 | help="Set the editor used by IPython (default to $EDITOR/vi/notepad)." |
|
271 | 271 | ) |
|
272 | 272 | pager = Unicode('less', config=True, |
|
273 | 273 | help="The shell program to be used for paging.") |
|
274 | 274 | |
|
275 | 275 | screen_length = Integer(0, config=True, |
|
276 | 276 | help= |
|
277 | 277 | """Number of lines of your screen, used to control printing of very |
|
278 | 278 | long strings. Strings longer than this number of lines will be sent |
|
279 | 279 | through a pager instead of directly printed. The default value for |
|
280 | 280 | this is 0, which means IPython will auto-detect your screen size every |
|
281 | 281 | time it needs to print certain potentially long strings (this doesn't |
|
282 | 282 | change the behavior of the 'print' keyword, it's only triggered |
|
283 | 283 | internally). If for some reason this isn't working well (it needs |
|
284 | 284 | curses support), specify it yourself. Otherwise don't change the |
|
285 | 285 | default.""", |
|
286 | 286 | ) |
|
287 | 287 | term_title = CBool(False, config=True, |
|
288 | 288 | help="Enable auto setting the terminal title." |
|
289 | 289 | ) |
|
290 | 290 | |
|
291 | 291 | # This `using_paste_magics` is used to detect whether the code is being |
|
292 | 292 | # executed via paste magics functions |
|
293 | 293 | using_paste_magics = CBool(False) |
|
294 | 294 | |
|
295 | 295 | # In the terminal, GUI control is done via PyOS_InputHook |
|
296 | 296 | @staticmethod |
|
297 | 297 | def enable_gui(gui=None, app=None): |
|
298 | 298 | """Switch amongst GUI input hooks by name. |
|
299 | 299 | """ |
|
300 | 300 | # Deferred import |
|
301 | 301 | from IPython.lib.inputhook import enable_gui as real_enable_gui |
|
302 | 302 | try: |
|
303 | 303 | return real_enable_gui(gui, app) |
|
304 | 304 | except ValueError as e: |
|
305 | 305 | raise UsageError("%s" % e) |
|
306 | 306 | |
|
307 | 307 | def __init__(self, config=None, ipython_dir=None, profile_dir=None, |
|
308 | 308 | user_ns=None, user_module=None, custom_exceptions=((),None), |
|
309 | 309 | usage=None, banner1=None, banner2=None, display_banner=None, |
|
310 | 310 | **kwargs): |
|
311 | 311 | |
|
312 | 312 | super(TerminalInteractiveShell, self).__init__( |
|
313 | 313 | config=config, ipython_dir=ipython_dir, profile_dir=profile_dir, user_ns=user_ns, |
|
314 | 314 | user_module=user_module, custom_exceptions=custom_exceptions, |
|
315 | 315 | **kwargs |
|
316 | 316 | ) |
|
317 | 317 | # use os.system instead of utils.process.system by default, |
|
318 | 318 | # because piped system doesn't make sense in the Terminal: |
|
319 | 319 | self.system = self.system_raw |
|
320 | 320 | |
|
321 | 321 | self.init_term_title() |
|
322 | 322 | self.init_usage(usage) |
|
323 | 323 | self.init_banner(banner1, banner2, display_banner) |
|
324 | 324 | |
|
325 | 325 | #------------------------------------------------------------------------- |
|
326 | 326 | # Overrides of init stages |
|
327 | 327 | #------------------------------------------------------------------------- |
|
328 | 328 | |
|
329 | 329 | def init_display_formatter(self): |
|
330 | 330 | super(TerminalInteractiveShell, self).init_display_formatter() |
|
331 | 331 | # terminal only supports plaintext |
|
332 | 332 | self.display_formatter.active_types = ['text/plain'] |
|
333 | 333 | |
|
334 | 334 | #------------------------------------------------------------------------- |
|
335 | 335 | # Things related to the terminal |
|
336 | 336 | #------------------------------------------------------------------------- |
|
337 | 337 | |
|
338 | 338 | @property |
|
339 | 339 | def usable_screen_length(self): |
|
340 | 340 | if self.screen_length == 0: |
|
341 | 341 | return 0 |
|
342 | 342 | else: |
|
343 | 343 | num_lines_bot = self.separate_in.count('\n')+1 |
|
344 | 344 | return self.screen_length - num_lines_bot |
|
345 | 345 | |
|
346 | 346 | def init_term_title(self): |
|
347 | 347 | # Enable or disable the terminal title. |
|
348 | 348 | if self.term_title: |
|
349 | 349 | toggle_set_term_title(True) |
|
350 | 350 | set_term_title('IPython: ' + abbrev_cwd()) |
|
351 | 351 | else: |
|
352 | 352 | toggle_set_term_title(False) |
|
353 | 353 | |
|
354 | 354 | #------------------------------------------------------------------------- |
|
355 | 355 | # Things related to aliases |
|
356 | 356 | #------------------------------------------------------------------------- |
|
357 | 357 | |
|
358 | 358 | def init_alias(self): |
|
359 | 359 | # The parent class defines aliases that can be safely used with any |
|
360 | 360 | # frontend. |
|
361 | 361 | super(TerminalInteractiveShell, self).init_alias() |
|
362 | 362 | |
|
363 | 363 | # Now define aliases that only make sense on the terminal, because they |
|
364 | 364 | # need direct access to the console in a way that we can't emulate in |
|
365 | 365 | # GUI or web frontend |
|
366 | 366 | if os.name == 'posix': |
|
367 | 367 | aliases = [('clear', 'clear'), ('more', 'more'), ('less', 'less'), |
|
368 | 368 | ('man', 'man')] |
|
369 | 369 | elif os.name == 'nt': |
|
370 | 370 | aliases = [('cls', 'cls')] |
|
371 | 371 | |
|
372 | 372 | |
|
373 | 373 | for name, cmd in aliases: |
|
374 | 374 | self.alias_manager.soft_define_alias(name, cmd) |
|
375 | 375 | |
|
376 | 376 | #------------------------------------------------------------------------- |
|
377 | 377 | # Things related to the banner and usage |
|
378 | 378 | #------------------------------------------------------------------------- |
|
379 | 379 | |
|
380 | 380 | def _banner1_changed(self): |
|
381 | 381 | self.compute_banner() |
|
382 | 382 | |
|
383 | 383 | def _banner2_changed(self): |
|
384 | 384 | self.compute_banner() |
|
385 | 385 | |
|
386 | 386 | def _term_title_changed(self, name, new_value): |
|
387 | 387 | self.init_term_title() |
|
388 | 388 | |
|
389 | 389 | def init_banner(self, banner1, banner2, display_banner): |
|
390 | 390 | if banner1 is not None: |
|
391 | 391 | self.banner1 = banner1 |
|
392 | 392 | if banner2 is not None: |
|
393 | 393 | self.banner2 = banner2 |
|
394 | 394 | if display_banner is not None: |
|
395 | 395 | self.display_banner = display_banner |
|
396 | 396 | self.compute_banner() |
|
397 | 397 | |
|
398 | 398 | def show_banner(self, banner=None): |
|
399 | 399 | if banner is None: |
|
400 | 400 | banner = self.banner |
|
401 | 401 | self.write(banner) |
|
402 | 402 | |
|
403 | 403 | def compute_banner(self): |
|
404 | 404 | self.banner = self.banner1 |
|
405 | 405 | if self.profile and self.profile != 'default': |
|
406 | 406 | self.banner += '\nIPython profile: %s\n' % self.profile |
|
407 | 407 | if self.banner2: |
|
408 | 408 | self.banner += '\n' + self.banner2 |
|
409 | 409 | |
|
410 | 410 | def init_usage(self, usage=None): |
|
411 | 411 | if usage is None: |
|
412 | 412 | self.usage = interactive_usage |
|
413 | 413 | else: |
|
414 | 414 | self.usage = usage |
|
415 | 415 | |
|
416 | 416 | #------------------------------------------------------------------------- |
|
417 | 417 | # Mainloop and code execution logic |
|
418 | 418 | #------------------------------------------------------------------------- |
|
419 | 419 | |
|
420 | 420 | def mainloop(self, display_banner=None): |
|
421 | 421 | """Start the mainloop. |
|
422 | 422 | |
|
423 | 423 | If an optional banner argument is given, it will override the |
|
424 | 424 | internally created default banner. |
|
425 | 425 | """ |
|
426 | 426 | |
|
427 | 427 | with self.builtin_trap, self.display_trap: |
|
428 | 428 | |
|
429 | 429 | while 1: |
|
430 | 430 | try: |
|
431 | 431 | self.interact(display_banner=display_banner) |
|
432 | 432 | #self.interact_with_readline() |
|
433 | 433 | # XXX for testing of a readline-decoupled repl loop, call |
|
434 | 434 | # interact_with_readline above |
|
435 | 435 | break |
|
436 | 436 | except KeyboardInterrupt: |
|
437 | 437 | # this should not be necessary, but KeyboardInterrupt |
|
438 | 438 | # handling seems rather unpredictable... |
|
439 | 439 | self.write("\nKeyboardInterrupt in interact()\n") |
|
440 | 440 | |
|
441 | 441 | def _replace_rlhist_multiline(self, source_raw, hlen_before_cell): |
|
442 | 442 | """Store multiple lines as a single entry in history""" |
|
443 | 443 | |
|
444 | 444 | # do nothing without readline or disabled multiline |
|
445 | 445 | if not self.has_readline or not self.multiline_history: |
|
446 | 446 | return hlen_before_cell |
|
447 | 447 | |
|
448 | 448 | # windows rl has no remove_history_item |
|
449 | 449 | if not hasattr(self.readline, "remove_history_item"): |
|
450 | 450 | return hlen_before_cell |
|
451 | 451 | |
|
452 | 452 | # skip empty cells |
|
453 | 453 | if not source_raw.rstrip(): |
|
454 | 454 | return hlen_before_cell |
|
455 | 455 | |
|
456 | 456 | # nothing changed do nothing, e.g. when rl removes consecutive dups |
|
457 | 457 | hlen = self.readline.get_current_history_length() |
|
458 | 458 | if hlen == hlen_before_cell: |
|
459 | 459 | return hlen_before_cell |
|
460 | 460 | |
|
461 | 461 | for i in range(hlen - hlen_before_cell): |
|
462 | 462 | self.readline.remove_history_item(hlen - i - 1) |
|
463 | 463 | stdin_encoding = get_stream_enc(sys.stdin, 'utf-8') |
|
464 | 464 | self.readline.add_history(py3compat.unicode_to_str(source_raw.rstrip(), |
|
465 | 465 | stdin_encoding)) |
|
466 | 466 | return self.readline.get_current_history_length() |
|
467 | 467 | |
|
468 | 468 | def interact(self, display_banner=None): |
|
469 | 469 | """Closely emulate the interactive Python console.""" |
|
470 | 470 | |
|
471 | 471 | # batch run -> do not interact |
|
472 | 472 | if self.exit_now: |
|
473 | 473 | return |
|
474 | 474 | |
|
475 | 475 | if display_banner is None: |
|
476 | 476 | display_banner = self.display_banner |
|
477 | 477 | |
|
478 | 478 | if isinstance(display_banner, py3compat.string_types): |
|
479 | 479 | self.show_banner(display_banner) |
|
480 | 480 | elif display_banner: |
|
481 | 481 | self.show_banner() |
|
482 | 482 | |
|
483 | 483 | more = False |
|
484 | 484 | |
|
485 | 485 | if self.has_readline: |
|
486 | 486 | self.readline_startup_hook(self.pre_readline) |
|
487 | 487 | hlen_b4_cell = self.readline.get_current_history_length() |
|
488 | 488 | else: |
|
489 | 489 | hlen_b4_cell = 0 |
|
490 | 490 | # exit_now is set by a call to %Exit or %Quit, through the |
|
491 | 491 | # ask_exit callback. |
|
492 | 492 | |
|
493 | 493 | while not self.exit_now: |
|
494 | 494 | self.hooks.pre_prompt_hook() |
|
495 | 495 | if more: |
|
496 | 496 | try: |
|
497 | 497 | prompt = self.prompt_manager.render('in2') |
|
498 | 498 | except: |
|
499 | 499 | self.showtraceback() |
|
500 | 500 | if self.autoindent: |
|
501 | 501 | self.rl_do_indent = True |
|
502 | 502 | |
|
503 | 503 | else: |
|
504 | 504 | try: |
|
505 | 505 | prompt = self.separate_in + self.prompt_manager.render('in') |
|
506 | 506 | except: |
|
507 | 507 | self.showtraceback() |
|
508 | 508 | try: |
|
509 | 509 | line = self.raw_input(prompt) |
|
510 | 510 | if self.exit_now: |
|
511 | 511 | # quick exit on sys.std[in|out] close |
|
512 | 512 | break |
|
513 | 513 | if self.autoindent: |
|
514 | 514 | self.rl_do_indent = False |
|
515 | 515 | |
|
516 | 516 | except KeyboardInterrupt: |
|
517 | 517 | #double-guard against keyboardinterrupts during kbdint handling |
|
518 | 518 | try: |
|
519 | 519 | self.write('\nKeyboardInterrupt\n') |
|
520 | 520 | source_raw = self.input_splitter.source_raw_reset()[1] |
|
521 | 521 | hlen_b4_cell = \ |
|
522 | 522 | self._replace_rlhist_multiline(source_raw, hlen_b4_cell) |
|
523 | 523 | more = False |
|
524 | 524 | except KeyboardInterrupt: |
|
525 | 525 | pass |
|
526 | 526 | except EOFError: |
|
527 | 527 | if self.autoindent: |
|
528 | 528 | self.rl_do_indent = False |
|
529 | 529 | if self.has_readline: |
|
530 | 530 | self.readline_startup_hook(None) |
|
531 | 531 | self.write('\n') |
|
532 | 532 | self.exit() |
|
533 | 533 | except bdb.BdbQuit: |
|
534 | 534 | warn('The Python debugger has exited with a BdbQuit exception.\n' |
|
535 | 535 | 'Because of how pdb handles the stack, it is impossible\n' |
|
536 | 536 | 'for IPython to properly format this particular exception.\n' |
|
537 | 537 | 'IPython will resume normal operation.') |
|
538 | 538 | except: |
|
539 | 539 | # exceptions here are VERY RARE, but they can be triggered |
|
540 | 540 | # asynchronously by signal handlers, for example. |
|
541 | 541 | self.showtraceback() |
|
542 | 542 | else: |
|
543 | 543 | self.input_splitter.push(line) |
|
544 | 544 | more = self.input_splitter.push_accepts_more() |
|
545 | 545 | if (self.SyntaxTB.last_syntax_error and |
|
546 | 546 | self.autoedit_syntax): |
|
547 | 547 | self.edit_syntax_error() |
|
548 | 548 | if not more: |
|
549 | 549 | source_raw = self.input_splitter.source_raw_reset()[1] |
|
550 | 550 | self.run_cell(source_raw, store_history=True) |
|
551 | 551 | hlen_b4_cell = \ |
|
552 | 552 | self._replace_rlhist_multiline(source_raw, hlen_b4_cell) |
|
553 | 553 | |
|
554 | 554 | # Turn off the exit flag, so the mainloop can be restarted if desired |
|
555 | 555 | self.exit_now = False |
|
556 | 556 | |
|
557 | 557 | def raw_input(self, prompt=''): |
|
558 | 558 | """Write a prompt and read a line. |
|
559 | 559 | |
|
560 | 560 | The returned line does not include the trailing newline. |
|
561 | 561 | When the user enters the EOF key sequence, EOFError is raised. |
|
562 | 562 | |
|
563 | Optional inputs: | |
|
563 | Parameters | |
|
564 | ---------- | |
|
564 | 565 | |
|
565 | - prompt(''): a string to be printed to prompt the user. | |
|
566 | ||
|
567 | - continue_prompt(False): whether this line is the first one or a | |
|
568 | continuation in a sequence of inputs. | |
|
566 | prompt : str, optional | |
|
567 | A string to be printed to prompt the user. | |
|
569 | 568 | """ |
|
570 | 569 | # Code run by the user may have modified the readline completer state. |
|
571 | 570 | # We must ensure that our completer is back in place. |
|
572 | 571 | |
|
573 | 572 | if self.has_readline: |
|
574 | 573 | self.set_readline_completer() |
|
575 | 574 | |
|
576 | 575 | # raw_input expects str, but we pass it unicode sometimes |
|
577 | 576 | prompt = py3compat.cast_bytes_py2(prompt) |
|
578 | 577 | |
|
579 | 578 | try: |
|
580 | 579 | line = py3compat.str_to_unicode(self.raw_input_original(prompt)) |
|
581 | 580 | except ValueError: |
|
582 | 581 | warn("\n********\nYou or a %run:ed script called sys.stdin.close()" |
|
583 | 582 | " or sys.stdout.close()!\nExiting IPython!\n") |
|
584 | 583 | self.ask_exit() |
|
585 | 584 | return "" |
|
586 | 585 | |
|
587 | 586 | # Try to be reasonably smart about not re-indenting pasted input more |
|
588 | 587 | # than necessary. We do this by trimming out the auto-indent initial |
|
589 | 588 | # spaces, if the user's actual input started itself with whitespace. |
|
590 | 589 | if self.autoindent: |
|
591 | 590 | if num_ini_spaces(line) > self.indent_current_nsp: |
|
592 | 591 | line = line[self.indent_current_nsp:] |
|
593 | 592 | self.indent_current_nsp = 0 |
|
594 | 593 | |
|
595 | 594 | return line |
|
596 | 595 | |
|
597 | 596 | #------------------------------------------------------------------------- |
|
598 | 597 | # Methods to support auto-editing of SyntaxErrors. |
|
599 | 598 | #------------------------------------------------------------------------- |
|
600 | 599 | |
|
601 | 600 | def edit_syntax_error(self): |
|
602 | 601 | """The bottom half of the syntax error handler called in the main loop. |
|
603 | 602 | |
|
604 | 603 | Loop until syntax error is fixed or user cancels. |
|
605 | 604 | """ |
|
606 | 605 | |
|
607 | 606 | while self.SyntaxTB.last_syntax_error: |
|
608 | 607 | # copy and clear last_syntax_error |
|
609 | 608 | err = self.SyntaxTB.clear_err_state() |
|
610 | 609 | if not self._should_recompile(err): |
|
611 | 610 | return |
|
612 | 611 | try: |
|
613 | 612 | # may set last_syntax_error again if a SyntaxError is raised |
|
614 | 613 | self.safe_execfile(err.filename,self.user_ns) |
|
615 | 614 | except: |
|
616 | 615 | self.showtraceback() |
|
617 | 616 | else: |
|
618 | 617 | try: |
|
619 | 618 | f = open(err.filename) |
|
620 | 619 | try: |
|
621 | 620 | # This should be inside a display_trap block and I |
|
622 | 621 | # think it is. |
|
623 | 622 | sys.displayhook(f.read()) |
|
624 | 623 | finally: |
|
625 | 624 | f.close() |
|
626 | 625 | except: |
|
627 | 626 | self.showtraceback() |
|
628 | 627 | |
|
629 | 628 | def _should_recompile(self,e): |
|
630 | 629 | """Utility routine for edit_syntax_error""" |
|
631 | 630 | |
|
632 | 631 | if e.filename in ('<ipython console>','<input>','<string>', |
|
633 | 632 | '<console>','<BackgroundJob compilation>', |
|
634 | 633 | None): |
|
635 | 634 | |
|
636 | 635 | return False |
|
637 | 636 | try: |
|
638 | 637 | if (self.autoedit_syntax and |
|
639 | 638 | not self.ask_yes_no('Return to editor to correct syntax error? ' |
|
640 | 639 | '[Y/n] ','y')): |
|
641 | 640 | return False |
|
642 | 641 | except EOFError: |
|
643 | 642 | return False |
|
644 | 643 | |
|
645 | 644 | def int0(x): |
|
646 | 645 | try: |
|
647 | 646 | return int(x) |
|
648 | 647 | except TypeError: |
|
649 | 648 | return 0 |
|
650 | 649 | # always pass integer line and offset values to editor hook |
|
651 | 650 | try: |
|
652 | 651 | self.hooks.fix_error_editor(e.filename, |
|
653 | 652 | int0(e.lineno),int0(e.offset),e.msg) |
|
654 | 653 | except TryNext: |
|
655 | 654 | warn('Could not open editor') |
|
656 | 655 | return False |
|
657 | 656 | return True |
|
658 | 657 | |
|
659 | 658 | #------------------------------------------------------------------------- |
|
660 | 659 | # Things related to exiting |
|
661 | 660 | #------------------------------------------------------------------------- |
|
662 | 661 | |
|
663 | 662 | def ask_exit(self): |
|
664 | 663 | """ Ask the shell to exit. Can be overiden and used as a callback. """ |
|
665 | 664 | self.exit_now = True |
|
666 | 665 | |
|
667 | 666 | def exit(self): |
|
668 | 667 | """Handle interactive exit. |
|
669 | 668 | |
|
670 | 669 | This method calls the ask_exit callback.""" |
|
671 | 670 | if self.confirm_exit: |
|
672 | 671 | if self.ask_yes_no('Do you really want to exit ([y]/n)?','y'): |
|
673 | 672 | self.ask_exit() |
|
674 | 673 | else: |
|
675 | 674 | self.ask_exit() |
|
676 | 675 | |
|
677 | 676 | #------------------------------------------------------------------------- |
|
678 | 677 | # Things related to magics |
|
679 | 678 | #------------------------------------------------------------------------- |
|
680 | 679 | |
|
681 | 680 | def init_magics(self): |
|
682 | 681 | super(TerminalInteractiveShell, self).init_magics() |
|
683 | 682 | self.register_magics(TerminalMagics) |
|
684 | 683 | |
|
685 | 684 | def showindentationerror(self): |
|
686 | 685 | super(TerminalInteractiveShell, self).showindentationerror() |
|
687 | 686 | if not self.using_paste_magics: |
|
688 | 687 | print("If you want to paste code into IPython, try the " |
|
689 | 688 | "%paste and %cpaste magic functions.") |
|
690 | 689 | |
|
691 | 690 | |
|
692 | 691 | InteractiveShellABC.register(TerminalInteractiveShell) |
@@ -1,382 +1,383 b'' | |||
|
1 | 1 | # -*- coding: utf-8 -*- |
|
2 | 2 | """Decorators for labeling test objects. |
|
3 | 3 | |
|
4 | 4 | Decorators that merely return a modified version of the original function |
|
5 | 5 | object are straightforward. Decorators that return a new function object need |
|
6 | 6 | to use nose.tools.make_decorator(original_function)(decorator) in returning the |
|
7 | 7 | decorator, in order to preserve metadata such as function name, setup and |
|
8 | 8 | teardown functions and so on - see nose.tools for more information. |
|
9 | 9 | |
|
10 | 10 | This module provides a set of useful decorators meant to be ready to use in |
|
11 | 11 | your own tests. See the bottom of the file for the ready-made ones, and if you |
|
12 | 12 | find yourself writing a new one that may be of generic use, add it here. |
|
13 | 13 | |
|
14 | 14 | Included decorators: |
|
15 | 15 | |
|
16 | 16 | |
|
17 | 17 | Lightweight testing that remains unittest-compatible. |
|
18 | 18 | |
|
19 | 19 | - An @as_unittest decorator can be used to tag any normal parameter-less |
|
20 | 20 | function as a unittest TestCase. Then, both nose and normal unittest will |
|
21 | 21 | recognize it as such. This will make it easier to migrate away from Nose if |
|
22 | 22 | we ever need/want to while maintaining very lightweight tests. |
|
23 | 23 | |
|
24 | 24 | NOTE: This file contains IPython-specific decorators. Using the machinery in |
|
25 | 25 | IPython.external.decorators, we import either numpy.testing.decorators if numpy is |
|
26 | 26 | available, OR use equivalent code in IPython.external._decorators, which |
|
27 | 27 | we've copied verbatim from numpy. |
|
28 | 28 | |
|
29 | 29 | Authors |
|
30 | 30 | ------- |
|
31 | 31 | |
|
32 | 32 | - Fernando Perez <Fernando.Perez@berkeley.edu> |
|
33 | 33 | """ |
|
34 | 34 | |
|
35 | 35 | #----------------------------------------------------------------------------- |
|
36 | 36 | # Copyright (C) 2009-2011 The IPython Development Team |
|
37 | 37 | # |
|
38 | 38 | # Distributed under the terms of the BSD License. The full license is in |
|
39 | 39 | # the file COPYING, distributed as part of this software. |
|
40 | 40 | #----------------------------------------------------------------------------- |
|
41 | 41 | |
|
42 | 42 | #----------------------------------------------------------------------------- |
|
43 | 43 | # Imports |
|
44 | 44 | #----------------------------------------------------------------------------- |
|
45 | 45 | |
|
46 | 46 | # Stdlib imports |
|
47 | 47 | import sys |
|
48 | 48 | import os |
|
49 | 49 | import tempfile |
|
50 | 50 | import unittest |
|
51 | 51 | |
|
52 | 52 | # Third-party imports |
|
53 | 53 | |
|
54 | 54 | # This is Michele Simionato's decorator module, kept verbatim. |
|
55 | 55 | from IPython.external.decorator import decorator |
|
56 | 56 | |
|
57 | 57 | # Expose the unittest-driven decorators |
|
58 | 58 | from .ipunittest import ipdoctest, ipdocstring |
|
59 | 59 | |
|
60 | 60 | # Grab the numpy-specific decorators which we keep in a file that we |
|
61 | 61 | # occasionally update from upstream: decorators.py is a copy of |
|
62 | 62 | # numpy.testing.decorators, we expose all of it here. |
|
63 | 63 | from IPython.external.decorators import * |
|
64 | 64 | |
|
65 | 65 | # For onlyif_cmd_exists decorator |
|
66 | 66 | from IPython.utils.process import is_cmd_found |
|
67 | 67 | from IPython.utils.py3compat import string_types |
|
68 | 68 | |
|
69 | 69 | #----------------------------------------------------------------------------- |
|
70 | 70 | # Classes and functions |
|
71 | 71 | #----------------------------------------------------------------------------- |
|
72 | 72 | |
|
73 | 73 | # Simple example of the basic idea |
|
74 | 74 | def as_unittest(func): |
|
75 | 75 | """Decorator to make a simple function into a normal test via unittest.""" |
|
76 | 76 | class Tester(unittest.TestCase): |
|
77 | 77 | def test(self): |
|
78 | 78 | func() |
|
79 | 79 | |
|
80 | 80 | Tester.__name__ = func.__name__ |
|
81 | 81 | |
|
82 | 82 | return Tester |
|
83 | 83 | |
|
84 | 84 | # Utility functions |
|
85 | 85 | |
|
86 | 86 | def apply_wrapper(wrapper,func): |
|
87 | 87 | """Apply a wrapper to a function for decoration. |
|
88 | 88 | |
|
89 | 89 | This mixes Michele Simionato's decorator tool with nose's make_decorator, |
|
90 | 90 | to apply a wrapper in a decorator so that all nose attributes, as well as |
|
91 | 91 | function signature and other properties, survive the decoration cleanly. |
|
92 | 92 | This will ensure that wrapped functions can still be well introspected via |
|
93 | 93 | IPython, for example. |
|
94 | 94 | """ |
|
95 | 95 | import nose.tools |
|
96 | 96 | |
|
97 | 97 | return decorator(wrapper,nose.tools.make_decorator(func)(wrapper)) |
|
98 | 98 | |
|
99 | 99 | |
|
100 | 100 | def make_label_dec(label,ds=None): |
|
101 | 101 | """Factory function to create a decorator that applies one or more labels. |
|
102 | 102 | |
|
103 | 103 | Parameters |
|
104 | 104 | ---------- |
|
105 | 105 | label : string or sequence |
|
106 | 106 | One or more labels that will be applied by the decorator to the functions |
|
107 | 107 | it decorates. Labels are attributes of the decorated function with their |
|
108 | 108 | value set to True. |
|
109 | 109 | |
|
110 | 110 | ds : string |
|
111 | 111 | An optional docstring for the resulting decorator. If not given, a |
|
112 | 112 | default docstring is auto-generated. |
|
113 | 113 | |
|
114 | 114 | Returns |
|
115 | 115 | ------- |
|
116 | 116 | A decorator. |
|
117 | 117 | |
|
118 | 118 | Examples |
|
119 | 119 | -------- |
|
120 | 120 | |
|
121 | 121 | A simple labeling decorator: |
|
122 | 122 | |
|
123 | 123 | >>> slow = make_label_dec('slow') |
|
124 | 124 | >>> slow.__doc__ |
|
125 | 125 | "Labels a test as 'slow'." |
|
126 | 126 | |
|
127 | 127 | And one that uses multiple labels and a custom docstring: |
|
128 | 128 | |
|
129 | 129 | >>> rare = make_label_dec(['slow','hard'], |
|
130 | 130 | ... "Mix labels 'slow' and 'hard' for rare tests.") |
|
131 | 131 | >>> rare.__doc__ |
|
132 | 132 | "Mix labels 'slow' and 'hard' for rare tests." |
|
133 | 133 | |
|
134 | 134 | Now, let's test using this one: |
|
135 | 135 | >>> @rare |
|
136 | 136 | ... def f(): pass |
|
137 | 137 | ... |
|
138 | 138 | >>> |
|
139 | 139 | >>> f.slow |
|
140 | 140 | True |
|
141 | 141 | >>> f.hard |
|
142 | 142 | True |
|
143 | 143 | """ |
|
144 | 144 | |
|
145 | 145 | if isinstance(label, string_types): |
|
146 | 146 | labels = [label] |
|
147 | 147 | else: |
|
148 | 148 | labels = label |
|
149 | 149 | |
|
150 | 150 | # Validate that the given label(s) are OK for use in setattr() by doing a |
|
151 | 151 | # dry run on a dummy function. |
|
152 | 152 | tmp = lambda : None |
|
153 | 153 | for label in labels: |
|
154 | 154 | setattr(tmp,label,True) |
|
155 | 155 | |
|
156 | 156 | # This is the actual decorator we'll return |
|
157 | 157 | def decor(f): |
|
158 | 158 | for label in labels: |
|
159 | 159 | setattr(f,label,True) |
|
160 | 160 | return f |
|
161 | 161 | |
|
162 | 162 | # Apply the user's docstring, or autogenerate a basic one |
|
163 | 163 | if ds is None: |
|
164 | 164 | ds = "Labels a test as %r." % label |
|
165 | 165 | decor.__doc__ = ds |
|
166 | 166 | |
|
167 | 167 | return decor |
|
168 | 168 | |
|
169 | 169 | |
|
170 | 170 | # Inspired by numpy's skipif, but uses the full apply_wrapper utility to |
|
171 | 171 | # preserve function metadata better and allows the skip condition to be a |
|
172 | 172 | # callable. |
|
173 | 173 | def skipif(skip_condition, msg=None): |
|
174 | 174 | ''' Make function raise SkipTest exception if skip_condition is true |
|
175 | 175 | |
|
176 | 176 | Parameters |
|
177 | 177 | ---------- |
|
178 | skip_condition : bool or callable. | |
|
179 | Flag to determine whether to skip test. If the condition is a | |
|
180 | callable, it is used at runtime to dynamically make the decision. This | |
|
181 | is useful for tests that may require costly imports, to delay the cost | |
|
182 | until the test suite is actually executed. | |
|
178 | ||
|
179 | skip_condition : bool or callable | |
|
180 | Flag to determine whether to skip test. If the condition is a | |
|
181 | callable, it is used at runtime to dynamically make the decision. This | |
|
182 | is useful for tests that may require costly imports, to delay the cost | |
|
183 | until the test suite is actually executed. | |
|
183 | 184 | msg : string |
|
184 |
|
|
|
185 | ||
|
186 | Returns | |
|
187 | ------- | |
|
188 | decorator : function | |
|
189 |
|
|
|
190 |
|
|
|
191 |
|
|
|
185 | Message to give on raising a SkipTest exception. | |
|
186 | ||
|
187 | Returns | |
|
188 | ------- | |
|
189 | decorator : function | |
|
190 | Decorator, which, when applied to a function, causes SkipTest | |
|
191 | to be raised when the skip_condition was True, and the function | |
|
192 | to be called normally otherwise. | |
|
192 | 193 | |
|
193 | 194 | Notes |
|
194 | 195 | ----- |
|
195 | 196 | You will see from the code that we had to further decorate the |
|
196 | 197 | decorator with the nose.tools.make_decorator function in order to |
|
197 | 198 | transmit function name, and various other metadata. |
|
198 | 199 | ''' |
|
199 | 200 | |
|
200 | 201 | def skip_decorator(f): |
|
201 | 202 | # Local import to avoid a hard nose dependency and only incur the |
|
202 | 203 | # import time overhead at actual test-time. |
|
203 | 204 | import nose |
|
204 | 205 | |
|
205 | 206 | # Allow for both boolean or callable skip conditions. |
|
206 | 207 | if callable(skip_condition): |
|
207 | 208 | skip_val = skip_condition |
|
208 | 209 | else: |
|
209 | 210 | skip_val = lambda : skip_condition |
|
210 | 211 | |
|
211 | 212 | def get_msg(func,msg=None): |
|
212 | 213 | """Skip message with information about function being skipped.""" |
|
213 | 214 | if msg is None: out = 'Test skipped due to test condition.' |
|
214 | 215 | else: out = msg |
|
215 | 216 | return "Skipping test: %s. %s" % (func.__name__,out) |
|
216 | 217 | |
|
217 | 218 | # We need to define *two* skippers because Python doesn't allow both |
|
218 | 219 | # return with value and yield inside the same function. |
|
219 | 220 | def skipper_func(*args, **kwargs): |
|
220 | 221 | """Skipper for normal test functions.""" |
|
221 | 222 | if skip_val(): |
|
222 | 223 | raise nose.SkipTest(get_msg(f,msg)) |
|
223 | 224 | else: |
|
224 | 225 | return f(*args, **kwargs) |
|
225 | 226 | |
|
226 | 227 | def skipper_gen(*args, **kwargs): |
|
227 | 228 | """Skipper for test generators.""" |
|
228 | 229 | if skip_val(): |
|
229 | 230 | raise nose.SkipTest(get_msg(f,msg)) |
|
230 | 231 | else: |
|
231 | 232 | for x in f(*args, **kwargs): |
|
232 | 233 | yield x |
|
233 | 234 | |
|
234 | 235 | # Choose the right skipper to use when building the actual generator. |
|
235 | 236 | if nose.util.isgenerator(f): |
|
236 | 237 | skipper = skipper_gen |
|
237 | 238 | else: |
|
238 | 239 | skipper = skipper_func |
|
239 | 240 | |
|
240 | 241 | return nose.tools.make_decorator(f)(skipper) |
|
241 | 242 | |
|
242 | 243 | return skip_decorator |
|
243 | 244 | |
|
244 | 245 | # A version with the condition set to true, common case just to attach a message |
|
245 | 246 | # to a skip decorator |
|
246 | 247 | def skip(msg=None): |
|
247 | 248 | """Decorator factory - mark a test function for skipping from test suite. |
|
248 | 249 | |
|
249 | 250 | Parameters |
|
250 | 251 | ---------- |
|
251 | 252 | msg : string |
|
252 | 253 | Optional message to be added. |
|
253 | 254 | |
|
254 | 255 | Returns |
|
255 | 256 | ------- |
|
256 | 257 | decorator : function |
|
257 | 258 | Decorator, which, when applied to a function, causes SkipTest |
|
258 | 259 | to be raised, with the optional message added. |
|
259 | 260 | """ |
|
260 | 261 | |
|
261 | 262 | return skipif(True,msg) |
|
262 | 263 | |
|
263 | 264 | |
|
264 | 265 | def onlyif(condition, msg): |
|
265 | 266 | """The reverse from skipif, see skipif for details.""" |
|
266 | 267 | |
|
267 | 268 | if callable(condition): |
|
268 | 269 | skip_condition = lambda : not condition() |
|
269 | 270 | else: |
|
270 | 271 | skip_condition = lambda : not condition |
|
271 | 272 | |
|
272 | 273 | return skipif(skip_condition, msg) |
|
273 | 274 | |
|
274 | 275 | #----------------------------------------------------------------------------- |
|
275 | 276 | # Utility functions for decorators |
|
276 | 277 | def module_not_available(module): |
|
277 | 278 | """Can module be imported? Returns true if module does NOT import. |
|
278 | 279 | |
|
279 | 280 | This is used to make a decorator to skip tests that require module to be |
|
280 | 281 | available, but delay the 'import numpy' to test execution time. |
|
281 | 282 | """ |
|
282 | 283 | try: |
|
283 | 284 | mod = __import__(module) |
|
284 | 285 | mod_not_avail = False |
|
285 | 286 | except ImportError: |
|
286 | 287 | mod_not_avail = True |
|
287 | 288 | |
|
288 | 289 | return mod_not_avail |
|
289 | 290 | |
|
290 | 291 | |
|
291 | 292 | def decorated_dummy(dec, name): |
|
292 | 293 | """Return a dummy function decorated with dec, with the given name. |
|
293 | 294 | |
|
294 | 295 | Examples |
|
295 | 296 | -------- |
|
296 | 297 | import IPython.testing.decorators as dec |
|
297 | 298 | setup = dec.decorated_dummy(dec.skip_if_no_x11, __name__) |
|
298 | 299 | """ |
|
299 | 300 | dummy = lambda: None |
|
300 | 301 | dummy.__name__ = name |
|
301 | 302 | return dec(dummy) |
|
302 | 303 | |
|
303 | 304 | #----------------------------------------------------------------------------- |
|
304 | 305 | # Decorators for public use |
|
305 | 306 | |
|
306 | 307 | # Decorators to skip certain tests on specific platforms. |
|
307 | 308 | skip_win32 = skipif(sys.platform == 'win32', |
|
308 | 309 | "This test does not run under Windows") |
|
309 | 310 | skip_linux = skipif(sys.platform.startswith('linux'), |
|
310 | 311 | "This test does not run under Linux") |
|
311 | 312 | skip_osx = skipif(sys.platform == 'darwin',"This test does not run under OS X") |
|
312 | 313 | |
|
313 | 314 | |
|
314 | 315 | # Decorators to skip tests if not on specific platforms. |
|
315 | 316 | skip_if_not_win32 = skipif(sys.platform != 'win32', |
|
316 | 317 | "This test only runs under Windows") |
|
317 | 318 | skip_if_not_linux = skipif(not sys.platform.startswith('linux'), |
|
318 | 319 | "This test only runs under Linux") |
|
319 | 320 | skip_if_not_osx = skipif(sys.platform != 'darwin', |
|
320 | 321 | "This test only runs under OSX") |
|
321 | 322 | |
|
322 | 323 | |
|
323 | 324 | _x11_skip_cond = (sys.platform not in ('darwin', 'win32') and |
|
324 | 325 | os.environ.get('DISPLAY', '') == '') |
|
325 | 326 | _x11_skip_msg = "Skipped under *nix when X11/XOrg not available" |
|
326 | 327 | |
|
327 | 328 | skip_if_no_x11 = skipif(_x11_skip_cond, _x11_skip_msg) |
|
328 | 329 | |
|
329 | 330 | # not a decorator itself, returns a dummy function to be used as setup |
|
330 | 331 | def skip_file_no_x11(name): |
|
331 | 332 | return decorated_dummy(skip_if_no_x11, name) if _x11_skip_cond else None |
|
332 | 333 | |
|
333 | 334 | # Other skip decorators |
|
334 | 335 | |
|
335 | 336 | # generic skip without module |
|
336 | 337 | skip_without = lambda mod: skipif(module_not_available(mod), "This test requires %s" % mod) |
|
337 | 338 | |
|
338 | 339 | skipif_not_numpy = skip_without('numpy') |
|
339 | 340 | |
|
340 | 341 | skipif_not_matplotlib = skip_without('matplotlib') |
|
341 | 342 | |
|
342 | 343 | skipif_not_sympy = skip_without('sympy') |
|
343 | 344 | |
|
344 | 345 | skip_known_failure = knownfailureif(True,'This test is known to fail') |
|
345 | 346 | |
|
346 | 347 | known_failure_py3 = knownfailureif(sys.version_info[0] >= 3, |
|
347 | 348 | 'This test is known to fail on Python 3.') |
|
348 | 349 | |
|
349 | 350 | # A null 'decorator', useful to make more readable code that needs to pick |
|
350 | 351 | # between different decorators based on OS or other conditions |
|
351 | 352 | null_deco = lambda f: f |
|
352 | 353 | |
|
353 | 354 | # Some tests only run where we can use unicode paths. Note that we can't just |
|
354 | 355 | # check os.path.supports_unicode_filenames, which is always False on Linux. |
|
355 | 356 | try: |
|
356 | 357 | f = tempfile.NamedTemporaryFile(prefix=u"tmpβ¬") |
|
357 | 358 | except UnicodeEncodeError: |
|
358 | 359 | unicode_paths = False |
|
359 | 360 | else: |
|
360 | 361 | unicode_paths = True |
|
361 | 362 | f.close() |
|
362 | 363 | |
|
363 | 364 | onlyif_unicode_paths = onlyif(unicode_paths, ("This test is only applicable " |
|
364 | 365 | "where we can use unicode in filenames.")) |
|
365 | 366 | |
|
366 | 367 | |
|
367 | 368 | def onlyif_cmds_exist(*commands): |
|
368 | 369 | """ |
|
369 | 370 | Decorator to skip test when at least one of `commands` is not found. |
|
370 | 371 | """ |
|
371 | 372 | for cmd in commands: |
|
372 | 373 | try: |
|
373 | 374 | if not is_cmd_found(cmd): |
|
374 | 375 | return skip("This test runs only if command '{0}' " |
|
375 | 376 | "is installed".format(cmd)) |
|
376 | 377 | except ImportError as e: |
|
377 | 378 | # is_cmd_found uses pywin32 on windows, which might not be available |
|
378 | 379 | if sys.platform == 'win32' and 'pywin32' in str(e): |
|
379 | 380 | return skip("This test runs only if pywin32 and command '{0}' " |
|
380 | 381 | "is installed".format(cmd)) |
|
381 | 382 | raise e |
|
382 | 383 | return null_deco |
@@ -1,621 +1,621 b'' | |||
|
1 | 1 | .. _issues_list_012: |
|
2 | 2 | |
|
3 | 3 | Issues closed in the 0.12 development cycle |
|
4 | 4 | =========================================== |
|
5 | 5 | |
|
6 | 6 | Issues closed in 0.12.1 |
|
7 | 7 | ----------------------- |
|
8 | 8 | |
|
9 | 9 | GitHub stats for bugfix release 0.12.1 (12/28/2011-04/16/2012), backporting |
|
10 | 10 | pull requests from 0.13. |
|
11 | 11 | |
|
12 | 12 | We closed a total of 71 issues: 44 pull requests and 27 issues; this is the |
|
13 | 13 | full list (generated with the script `tools/github_stats.py`). |
|
14 | 14 | |
|
15 | 15 | This list is automatically generated, and may be incomplete: |
|
16 | 16 | |
|
17 | 17 | Pull Requests (44): |
|
18 | 18 | |
|
19 | 19 | * :ghpull:`1175`: core.completer: Clean up excessive and unused code. |
|
20 | 20 | * :ghpull:`1187`: misc notebook: connection file cleanup, first heartbeat, startup flush |
|
21 | 21 | * :ghpull:`1190`: Fix link to Chris Fonnesbeck blog post about 0.11 highlights. |
|
22 | 22 | * :ghpull:`1196`: docs: looks like a file path might have been accidentally pasted in the middle of a word |
|
23 | 23 | * :ghpull:`1206`: don't preserve fixConsole output in json |
|
24 | 24 | * :ghpull:`1207`: fix loadpy duplicating newlines |
|
25 | 25 | * :ghpull:`1213`: BUG: Minor typo in history_console_widget.py |
|
26 | 26 | * :ghpull:`1218`: Added -q option to %prun for suppression of the output, along with editing the dochelp string. |
|
27 | 27 | * :ghpull:`1222`: allow Reference as callable in map/apply |
|
28 | 28 | * :ghpull:`1229`: Fix display of SyntaxError in Python 3 |
|
29 | 29 | * :ghpull:`1246`: Skip tests that require X, when importing pylab results in RuntimeError. |
|
30 | 30 | * :ghpull:`1253`: set auto_create flag for notebook apps |
|
31 | 31 | * :ghpull:`1257`: use self.kernel_manager_class in qtconsoleapp |
|
32 | 32 | * :ghpull:`1262`: Heartbeat no longer shares the app's Context |
|
33 | 33 | * :ghpull:`1283`: HeartMonitor.period should be an Integer |
|
34 | 34 | * :ghpull:`1284`: a fix for GH 1269 |
|
35 | 35 | * :ghpull:`1289`: Make autoreload extension work on Python 3. |
|
36 | 36 | * :ghpull:`1306`: Fix %prun input parsing for escaped characters (closes #1302) |
|
37 | 37 | * :ghpull:`1312`: minor heartbeat tweaks |
|
38 | 38 | * :ghpull:`1318`: make Ctrl-D in qtconsole act same as in terminal (ready to merge) |
|
39 | 39 | * :ghpull:`1341`: Don't attempt to tokenize binary files for tracebacks |
|
40 | 40 | * :ghpull:`1353`: Save notebook as script using unicode file handle. |
|
41 | 41 | * :ghpull:`1363`: Fix some minor color/style config issues in the qtconsole |
|
42 | 42 | * :ghpull:`1364`: avoid jsonlib returning Decimal |
|
43 | 43 | * :ghpull:`1369`: load header with engine id when engine dies in TaskScheduler |
|
44 | 44 | * :ghpull:`1370`: allow draft76 websockets (Safari) |
|
45 | 45 | * :ghpull:`1374`: remove calls to meaningless ZMQStream.on_err |
|
46 | 46 | * :ghpull:`1377`: Saving non-ascii history |
|
47 | 47 | * :ghpull:`1396`: Fix for %tb magic. |
|
48 | 48 | * :ghpull:`1402`: fix symlinked /home issue for FreeBSD |
|
49 | 49 | * :ghpull:`1413`: get_home_dir expands symlinks, adjust test accordingly |
|
50 | 50 | * :ghpull:`1414`: ignore errors in shell.var_expand |
|
51 | 51 | * :ghpull:`1430`: Fix for tornado check for tornado < 1.1.0 |
|
52 | 52 | * :ghpull:`1445`: Don't build sphinx docs for sdists |
|
53 | 53 | * :ghpull:`1463`: Fix completion when importing modules in the cwd. |
|
54 | 54 | * :ghpull:`1477`: fix dangling `buffer` in IPython.parallel.util |
|
55 | 55 | * :ghpull:`1495`: BUG: Fix pretty-printing for overzealous objects |
|
56 | 56 | * :ghpull:`1496`: BUG: LBYL when clearing the output history on shutdown. |
|
57 | 57 | * :ghpull:`1514`: DOC: Fix references to IPython.lib.pretty instead of the old location |
|
58 | 58 | * :ghpull:`1517`: Fix indentation bug in IPython/lib/pretty.py |
|
59 | 59 | * :ghpull:`1538`: store git commit hash in utils._sysinfo instead of hidden data file |
|
60 | 60 | * :ghpull:`1599`: Fix for %run -d in Python 3 |
|
61 | 61 | * :ghpull:`1602`: Fix %env for Python 3 |
|
62 | 62 | * :ghpull:`1607`: cleanup sqlitedb temporary db file after tests |
|
63 | 63 | |
|
64 | 64 | Issues (27): |
|
65 | 65 | |
|
66 | 66 | * :ghissue:`676`: IPython.embed() from ipython crashes twice on exit |
|
67 | 67 | * :ghissue:`846`: Autoreload extension doesn't work with Python 3.2 |
|
68 | 68 | * :ghissue:`1187`: misc notebook: connection file cleanup, first heartbeat, startup flush |
|
69 | 69 | * :ghissue:`1191`: profile/startup files not executed with "notebook" |
|
70 | 70 | * :ghissue:`1197`: Interactive shell trying to: from ... import history |
|
71 | 71 | * :ghissue:`1198`: Kernel Has Died error in Notebook |
|
72 | 72 | * :ghissue:`1201`: %env magic fails with Python 3.2 |
|
73 | 73 | * :ghissue:`1204`: double newline from %loadpy in python notebook (at least on mac) |
|
74 | 74 | * :ghissue:`1208`: should dv.sync_import print failed imports ? |
|
75 | 75 | * :ghissue:`1225`: SyntaxError display broken in Python 3 |
|
76 | 76 | * :ghissue:`1232`: Dead kernel loop |
|
77 | 77 | * :ghissue:`1241`: When our debugger class is used standalone `_oh` key errors are thrown |
|
78 | 78 | * :ghissue:`1254`: typo in notebooklist.js breaks links |
|
79 | 79 | * :ghissue:`1260`: heartbeat failure on long gil-holding operation |
|
80 | 80 | * :ghissue:`1268`: notebook %reset magic fails with StdinNotImplementedError |
|
81 | 81 | * :ghissue:`1269`: Another strange input handling error |
|
82 | 82 | * :ghissue:`1281`: in Hub: registration_timeout must be an integer, but heartmonitor.period is CFloat |
|
83 | 83 | * :ghissue:`1302`: Input parsing with %prun clobbers escapes |
|
84 | 84 | * :ghissue:`1304`: controller/server load can disrupt heartbeat |
|
85 | 85 | * :ghissue:`1317`: Very slow traceback construction from Cython extension |
|
86 | 86 | * :ghissue:`1345`: notebook can't save unicode as script |
|
87 | 87 | * :ghissue:`1375`: %history -g -f file encoding issue |
|
88 | 88 | * :ghissue:`1401`: numpy arrays cannot be used with View.apply() in Python 3 |
|
89 | 89 | * :ghissue:`1408`: test_get_home_dir_3 failed on Mac OS X |
|
90 | 90 | * :ghissue:`1412`: Input parsing issue with %prun |
|
91 | 91 | * :ghissue:`1421`: ipython32 %run -d breaks with NameError name 'execfile' is not defined |
|
92 | 92 | * :ghissue:`1484`: unhide .git_commit_info.ini |
|
93 | 93 | |
|
94 | 94 | |
|
95 | 95 | Issues closed in 0.12 |
|
96 | 96 | --------------------- |
|
97 | 97 | |
|
98 | 98 | In this cycle, from August 1 to December 28 2011, we closed a total of 515 |
|
99 | 99 | issues, 257 pull requests and 258 regular issues; this is the full list |
|
100 | 100 | (generated with the script `tools/github_stats.py`). |
|
101 | 101 | |
|
102 | 102 | Pull requests (257): |
|
103 | 103 | |
|
104 | 104 | * `1174 <https://github.com/ipython/ipython/issues/1174>`_: Remove %install_default_config and %install_profiles |
|
105 | 105 | * `1178 <https://github.com/ipython/ipython/issues/1178>`_: Correct string type casting in pinfo. |
|
106 | 106 | * `1096 <https://github.com/ipython/ipython/issues/1096>`_: Show class init and call tooltips in notebook |
|
107 | 107 | * `1176 <https://github.com/ipython/ipython/issues/1176>`_: Modifications to profile list |
|
108 | 108 | * `1173 <https://github.com/ipython/ipython/issues/1173>`_: don't load gui/pylab in console frontend |
|
109 | 109 | * `1168 <https://github.com/ipython/ipython/issues/1168>`_: Add --script flag as shorthand for notebook save_script option. |
|
110 | 110 | * `1165 <https://github.com/ipython/ipython/issues/1165>`_: encode image_tag as utf8 in [x]html export |
|
111 | 111 | * `1161 <https://github.com/ipython/ipython/issues/1161>`_: Allow %loadpy to load remote URLs that don't end in .py |
|
112 | 112 | * `1158 <https://github.com/ipython/ipython/issues/1158>`_: Add coding header when notebook exported to .py file. |
|
113 | 113 | * `1160 <https://github.com/ipython/ipython/issues/1160>`_: don't ignore ctrl-C during `%gui qt` |
|
114 | 114 | * `1159 <https://github.com/ipython/ipython/issues/1159>`_: Add encoding header to Python files downloaded from notebooks. |
|
115 | 115 | * `1155 <https://github.com/ipython/ipython/issues/1155>`_: minor post-execute fixes (#1154) |
|
116 | 116 | * `1153 <https://github.com/ipython/ipython/issues/1153>`_: Pager tearing bug |
|
117 | 117 | * `1152 <https://github.com/ipython/ipython/issues/1152>`_: Add support for displaying maptlotlib axes directly. |
|
118 | 118 | * `1079 <https://github.com/ipython/ipython/issues/1079>`_: Login/out button cleanups |
|
119 | 119 | * `1151 <https://github.com/ipython/ipython/issues/1151>`_: allow access to user_ns in prompt_manager |
|
120 | 120 | * `1120 <https://github.com/ipython/ipython/issues/1120>`_: updated vim-ipython (pending) |
|
121 | 121 | * `1150 <https://github.com/ipython/ipython/issues/1150>`_: BUG: Scrolling pager in vsplit on Mac OSX tears. |
|
122 | 122 | * `1149 <https://github.com/ipython/ipython/issues/1149>`_: #1148 (win32 arg_split) |
|
123 | 123 | * `1147 <https://github.com/ipython/ipython/issues/1147>`_: Put qtconsole forground when launching |
|
124 | 124 | * `1146 <https://github.com/ipython/ipython/issues/1146>`_: allow saving notebook.py next to notebook.ipynb |
|
125 | 125 | * `1128 <https://github.com/ipython/ipython/issues/1128>`_: fix pylab StartMenu item |
|
126 | 126 | * `1140 <https://github.com/ipython/ipython/issues/1140>`_: Namespaces for embedding |
|
127 | 127 | * `1132 <https://github.com/ipython/ipython/issues/1132>`_: [notebook] read-only: disable name field |
|
128 | 128 | * `1125 <https://github.com/ipython/ipython/issues/1125>`_: notebook : update logo |
|
129 | 129 | * `1135 <https://github.com/ipython/ipython/issues/1135>`_: allow customized template and static file paths for the notebook web app |
|
130 | 130 | * `1122 <https://github.com/ipython/ipython/issues/1122>`_: BUG: Issue #755 qt IPythonWidget.execute_file fails if filename contains... |
|
131 | 131 | * `1137 <https://github.com/ipython/ipython/issues/1137>`_: rename MPIExecLaunchers to MPILaunchers |
|
132 | 132 | * `1130 <https://github.com/ipython/ipython/issues/1130>`_: optionally ignore shlex's ValueError in arg_split |
|
133 | 133 | * `1116 <https://github.com/ipython/ipython/issues/1116>`_: Shlex unicode |
|
134 | 134 | * `1073 <https://github.com/ipython/ipython/issues/1073>`_: Storemagic plugin |
|
135 | 135 | * `1143 <https://github.com/ipython/ipython/issues/1143>`_: Add post_install script to create start menu entries in Python 3 |
|
136 | 136 | * `1138 <https://github.com/ipython/ipython/issues/1138>`_: Fix tests to work when ~/.config/ipython contains a symlink. |
|
137 | 137 | * `1121 <https://github.com/ipython/ipython/issues/1121>`_: Don't transform function calls on IPyAutocall objects |
|
138 | 138 | * `1118 <https://github.com/ipython/ipython/issues/1118>`_: protect CRLF from carriage-return action |
|
139 | 139 | * `1105 <https://github.com/ipython/ipython/issues/1105>`_: Fix for prompts containing newlines. |
|
140 | 140 | * `1126 <https://github.com/ipython/ipython/issues/1126>`_: Totally remove pager when read only (notebook) |
|
141 | 141 | * `1091 <https://github.com/ipython/ipython/issues/1091>`_: qtconsole : allow copy with shortcut in pager |
|
142 | 142 | * `1114 <https://github.com/ipython/ipython/issues/1114>`_: fix magics history in two-process ipython console |
|
143 |
* `1113 <https://github.com/ipython/ipython/issues/1113>`_: Fixing #1112 removing failing asserts for test_carriage_return and test_ |
|
|
143 | * `1113 <https://github.com/ipython/ipython/issues/1113>`_: Fixing #1112 removing failing asserts for test_carriage_return and test_beep | |
|
144 | 144 | * `1089 <https://github.com/ipython/ipython/issues/1089>`_: Support carriage return ('\r') and beep ('\b') characters in the qtconsole |
|
145 | 145 | * `1108 <https://github.com/ipython/ipython/issues/1108>`_: Completer usability 2 (rebased of pr #1082) |
|
146 | 146 | * `864 <https://github.com/ipython/ipython/issues/864>`_: Two-process terminal frontend (ipython core branch) |
|
147 | 147 | * `1082 <https://github.com/ipython/ipython/issues/1082>`_: usability and cross browser compat for completer |
|
148 | 148 | * `1053 <https://github.com/ipython/ipython/issues/1053>`_: minor improvements to text placement in qtconsole |
|
149 | 149 | * `1106 <https://github.com/ipython/ipython/issues/1106>`_: Fix display of errors in compiled code on Python 3 |
|
150 | 150 | * `1077 <https://github.com/ipython/ipython/issues/1077>`_: allow the notebook to run without MathJax |
|
151 | 151 | * `1072 <https://github.com/ipython/ipython/issues/1072>`_: If object has a getdoc() method, override its normal docstring. |
|
152 | 152 | * `1059 <https://github.com/ipython/ipython/issues/1059>`_: Switch to simple `__IPYTHON__` global |
|
153 | 153 | * `1070 <https://github.com/ipython/ipython/issues/1070>`_: Execution count after SyntaxError |
|
154 | 154 | * `1098 <https://github.com/ipython/ipython/issues/1098>`_: notebook: config section UI |
|
155 | 155 | * `1101 <https://github.com/ipython/ipython/issues/1101>`_: workaround spawnb missing from pexpect.__all__ |
|
156 | 156 | * `1097 <https://github.com/ipython/ipython/issues/1097>`_: typo, should fix #1095 |
|
157 | 157 | * `1099 <https://github.com/ipython/ipython/issues/1099>`_: qtconsole export xhtml/utf8 |
|
158 | 158 | * `1083 <https://github.com/ipython/ipython/issues/1083>`_: Prompts |
|
159 | 159 | * `1081 <https://github.com/ipython/ipython/issues/1081>`_: Fix wildcard search for updated namespaces |
|
160 | 160 | * `1084 <https://github.com/ipython/ipython/issues/1084>`_: write busy in notebook window title... |
|
161 | 161 | * `1078 <https://github.com/ipython/ipython/issues/1078>`_: PromptManager fixes |
|
162 | 162 | * `1064 <https://github.com/ipython/ipython/issues/1064>`_: Win32 shlex |
|
163 | 163 | * `1069 <https://github.com/ipython/ipython/issues/1069>`_: As you type completer, fix on Firefox |
|
164 | 164 | * `1039 <https://github.com/ipython/ipython/issues/1039>`_: Base of an as you type completer. |
|
165 | 165 | * `1065 <https://github.com/ipython/ipython/issues/1065>`_: Qtconsole fix racecondition |
|
166 | 166 | * `507 <https://github.com/ipython/ipython/issues/507>`_: Prompt manager |
|
167 | 167 | * `1056 <https://github.com/ipython/ipython/issues/1056>`_: Warning in code. qtconsole ssh -X |
|
168 | 168 | * `1036 <https://github.com/ipython/ipython/issues/1036>`_: Clean up javascript based on js2-mode feedback. |
|
169 | 169 | * `1052 <https://github.com/ipython/ipython/issues/1052>`_: Pylab fix |
|
170 | 170 | * `648 <https://github.com/ipython/ipython/issues/648>`_: Usermod |
|
171 | 171 | * `969 <https://github.com/ipython/ipython/issues/969>`_: Pexpect-u |
|
172 | 172 | * `1007 <https://github.com/ipython/ipython/issues/1007>`_: Fix paste/cpaste bug and refactor/cleanup that code a lot. |
|
173 | 173 | * `506 <https://github.com/ipython/ipython/issues/506>`_: make ENTER on a previous input field replace current input buffer |
|
174 | 174 | * `1040 <https://github.com/ipython/ipython/issues/1040>`_: json/jsonapi cleanup |
|
175 | 175 | * `1042 <https://github.com/ipython/ipython/issues/1042>`_: fix firefox (windows) break line on empty prompt number |
|
176 | 176 | * `1015 <https://github.com/ipython/ipython/issues/1015>`_: emacs freezes when tab is hit in ipython with latest python-mode |
|
177 | 177 | * `1023 <https://github.com/ipython/ipython/issues/1023>`_: flush stdout/stderr at the end of kernel init |
|
178 | 178 | * `956 <https://github.com/ipython/ipython/issues/956>`_: Generate "All magics..." menu live |
|
179 | 179 | * `1038 <https://github.com/ipython/ipython/issues/1038>`_: Notebook: don't change cell when selecting code using shift+up/down. |
|
180 | 180 | * `987 <https://github.com/ipython/ipython/issues/987>`_: Add Tooltip to notebook. |
|
181 | 181 | * `1028 <https://github.com/ipython/ipython/issues/1028>`_: Cleaner minimum version comparison |
|
182 | 182 | * `998 <https://github.com/ipython/ipython/issues/998>`_: defer to stdlib for path.get_home_dir() |
|
183 | 183 | * `1033 <https://github.com/ipython/ipython/issues/1033>`_: update copyright to 2011/20xx-2011 |
|
184 | 184 | * `1032 <https://github.com/ipython/ipython/issues/1032>`_: Intercept <esc> avoid closing websocket on Firefox |
|
185 | 185 | * `1030 <https://github.com/ipython/ipython/issues/1030>`_: use pyzmq tools where appropriate |
|
186 | 186 | * `1029 <https://github.com/ipython/ipython/issues/1029>`_: Restore pspersistence, including %store magic, as an extension. |
|
187 | 187 | * `1025 <https://github.com/ipython/ipython/issues/1025>`_: Dollar escape |
|
188 | 188 | * `999 <https://github.com/ipython/ipython/issues/999>`_: Fix issue #880 - more useful message to user when %paste fails |
|
189 | 189 | * `938 <https://github.com/ipython/ipython/issues/938>`_: changes to get ipython.el to work with the latest python-mode.el |
|
190 | 190 | * `1012 <https://github.com/ipython/ipython/issues/1012>`_: Add logout button. |
|
191 | 191 | * `1020 <https://github.com/ipython/ipython/issues/1020>`_: Dollar formatter for ! shell calls |
|
192 | 192 | * `1019 <https://github.com/ipython/ipython/issues/1019>`_: Use repr() to make quoted strings |
|
193 | 193 | * `1008 <https://github.com/ipython/ipython/issues/1008>`_: don't use crash_handler by default |
|
194 | 194 | * `1003 <https://github.com/ipython/ipython/issues/1003>`_: Drop consecutive duplicates when refilling readline history |
|
195 | 195 | * `997 <https://github.com/ipython/ipython/issues/997>`_: don't unregister interrupted post-exec functions |
|
196 | 196 | * `996 <https://github.com/ipython/ipython/issues/996>`_: add Integer traitlet |
|
197 | 197 | * `1016 <https://github.com/ipython/ipython/issues/1016>`_: Fix password hashing for Python 3 |
|
198 | 198 | * `1014 <https://github.com/ipython/ipython/issues/1014>`_: escape minus signs in manpages |
|
199 | 199 | * `1013 <https://github.com/ipython/ipython/issues/1013>`_: [NumPyExampleDocstring] link was pointing to raw file |
|
200 | 200 | * `1011 <https://github.com/ipython/ipython/issues/1011>`_: Add hashed password support. |
|
201 | 201 | * `1005 <https://github.com/ipython/ipython/issues/1005>`_: Quick fix for os.system requiring str parameter |
|
202 | 202 | * `994 <https://github.com/ipython/ipython/issues/994>`_: Allow latex formulas in HTML output |
|
203 | 203 | * `955 <https://github.com/ipython/ipython/issues/955>`_: Websocket Adjustments |
|
204 | 204 | * `979 <https://github.com/ipython/ipython/issues/979>`_: use system_raw in terminal, even on Windows |
|
205 | 205 | * `989 <https://github.com/ipython/ipython/issues/989>`_: fix arguments for commands in _process_posix |
|
206 | 206 | * `991 <https://github.com/ipython/ipython/issues/991>`_: Show traceback, continuing to start kernel if pylab init fails |
|
207 | 207 | * `981 <https://github.com/ipython/ipython/issues/981>`_: Split likely multiline text when writing JSON notebooks |
|
208 | 208 | * `957 <https://github.com/ipython/ipython/issues/957>`_: allow change of png DPI in inline backend |
|
209 | 209 | * `968 <https://github.com/ipython/ipython/issues/968>`_: add wantDirectory to ipdoctest, so that directories will be checked for e |
|
210 | 210 | * `984 <https://github.com/ipython/ipython/issues/984>`_: Do not expose variables defined at startup to %who etc. |
|
211 | 211 | * `985 <https://github.com/ipython/ipython/issues/985>`_: Fixes for parallel code on Python 3 |
|
212 | 212 | * `963 <https://github.com/ipython/ipython/issues/963>`_: disable calltips in PySide < 1.0.7 to prevent segfault |
|
213 | 213 | * `976 <https://github.com/ipython/ipython/issues/976>`_: Getting started on what's new |
|
214 | 214 | * `929 <https://github.com/ipython/ipython/issues/929>`_: Multiline history |
|
215 | 215 | * `964 <https://github.com/ipython/ipython/issues/964>`_: Default profile |
|
216 | 216 | * `961 <https://github.com/ipython/ipython/issues/961>`_: Disable the pager for the test suite |
|
217 | 217 | * `953 <https://github.com/ipython/ipython/issues/953>`_: Physics extension |
|
218 | 218 | * `950 <https://github.com/ipython/ipython/issues/950>`_: Add directory for startup files |
|
219 | 219 | * `940 <https://github.com/ipython/ipython/issues/940>`_: allow setting HistoryManager.hist_file with config |
|
220 | 220 | * `948 <https://github.com/ipython/ipython/issues/948>`_: Monkeypatch Tornado 2.1.1 so it works with Google Chrome 16. |
|
221 | 221 | * `916 <https://github.com/ipython/ipython/issues/916>`_: Run p ( https://github.com/ipython/ipython/pull/901 ) |
|
222 | 222 | * `923 <https://github.com/ipython/ipython/issues/923>`_: %config magic |
|
223 | 223 | * `920 <https://github.com/ipython/ipython/issues/920>`_: unordered iteration of AsyncMapResults (+ a couple fixes) |
|
224 | 224 | * `941 <https://github.com/ipython/ipython/issues/941>`_: Follow-up to 387dcd6a, `_rl.__doc__` is `None` with pyreadline |
|
225 | 225 | * `931 <https://github.com/ipython/ipython/issues/931>`_: read-only notebook mode |
|
226 | 226 | * `921 <https://github.com/ipython/ipython/issues/921>`_: Show invalid config message on TraitErrors during init |
|
227 | 227 | * `815 <https://github.com/ipython/ipython/issues/815>`_: Fix #481 using custom qt4 input hook |
|
228 | 228 | * `936 <https://github.com/ipython/ipython/issues/936>`_: Start webbrowser in a thread. Prevents lockup with Chrome. |
|
229 | 229 | * `937 <https://github.com/ipython/ipython/issues/937>`_: add dirty trick for readline import on OSX |
|
230 | 230 | * `913 <https://github.com/ipython/ipython/issues/913>`_: Py3 tests2 |
|
231 | 231 | * `933 <https://github.com/ipython/ipython/issues/933>`_: Cancel in qt console closeevent should trigger event.ignore() |
|
232 | 232 | * `930 <https://github.com/ipython/ipython/issues/930>`_: read-only notebook mode |
|
233 | 233 | * `910 <https://github.com/ipython/ipython/issues/910>`_: Make import checks more explicit in %whos |
|
234 | 234 | * `926 <https://github.com/ipython/ipython/issues/926>`_: reincarnate -V cmdline option |
|
235 | 235 | * `928 <https://github.com/ipython/ipython/issues/928>`_: BUG: Set context for font size change shortcuts in ConsoleWidget |
|
236 | 236 | * `901 <https://github.com/ipython/ipython/issues/901>`_: - There is a bug when running the profiler in the magic command (prun) with python3 |
|
237 | 237 | * `912 <https://github.com/ipython/ipython/issues/912>`_: Add magic for cls on windows. Fix for #181. |
|
238 | 238 | * `905 <https://github.com/ipython/ipython/issues/905>`_: enable %gui/%pylab magics in the Kernel |
|
239 | 239 | * `909 <https://github.com/ipython/ipython/issues/909>`_: Allow IPython to run without sqlite3 |
|
240 | 240 | * `887 <https://github.com/ipython/ipython/issues/887>`_: Qtconsole menu |
|
241 | 241 | * `895 <https://github.com/ipython/ipython/issues/895>`_: notebook download implies save |
|
242 | 242 | * `896 <https://github.com/ipython/ipython/issues/896>`_: Execfile |
|
243 | 243 | * `899 <https://github.com/ipython/ipython/issues/899>`_: Brian's Notebook work |
|
244 | 244 | * `892 <https://github.com/ipython/ipython/issues/892>`_: don't close figures every cycle with inline matplotlib backend |
|
245 | 245 | * `893 <https://github.com/ipython/ipython/issues/893>`_: Adding clear_output to kernel and HTML notebook |
|
246 | 246 | * `789 <https://github.com/ipython/ipython/issues/789>`_: Adding clear_output to kernel and HTML notebook. |
|
247 | 247 | * `898 <https://github.com/ipython/ipython/issues/898>`_: Don't pass unicode sys.argv with %run or `ipython script.py` |
|
248 | 248 | * `897 <https://github.com/ipython/ipython/issues/897>`_: Add tooltips to the notebook via 'title' attr. |
|
249 | 249 | * `877 <https://github.com/ipython/ipython/issues/877>`_: partial fix for issue #678 |
|
250 | 250 | * `838 <https://github.com/ipython/ipython/issues/838>`_: reenable multiline history for terminals |
|
251 | 251 | * `872 <https://github.com/ipython/ipython/issues/872>`_: The constructor of Client() checks for AssertionError in validate_url to open a file instead of connection to a URL if it fails. |
|
252 | 252 | * `884 <https://github.com/ipython/ipython/issues/884>`_: Notebook usability fixes |
|
253 | 253 | * `883 <https://github.com/ipython/ipython/issues/883>`_: User notification if notebook saving fails |
|
254 | 254 | * `889 <https://github.com/ipython/ipython/issues/889>`_: Add drop_by_id method to shell, to remove variables added by extensions. |
|
255 | 255 | * `891 <https://github.com/ipython/ipython/issues/891>`_: Ability to open the notebook in a browser when it starts |
|
256 | 256 | * `813 <https://github.com/ipython/ipython/issues/813>`_: Create menu bar for qtconsole |
|
257 | 257 | * `876 <https://github.com/ipython/ipython/issues/876>`_: protect IPython from bad custom exception handlers |
|
258 | 258 | * `856 <https://github.com/ipython/ipython/issues/856>`_: Backgroundjobs |
|
259 | 259 | * `868 <https://github.com/ipython/ipython/issues/868>`_: Warn user if MathJax can't be fetched from notebook closes #744 |
|
260 | 260 | * `878 <https://github.com/ipython/ipython/issues/878>`_: store_history=False default for run_cell |
|
261 | 261 | * `824 <https://github.com/ipython/ipython/issues/824>`_: History access |
|
262 | 262 | * `850 <https://github.com/ipython/ipython/issues/850>`_: Update codemirror to 2.15 and make the code internally more version-agnostic |
|
263 | 263 | * `861 <https://github.com/ipython/ipython/issues/861>`_: Fix for issue #56 |
|
264 | 264 | * `819 <https://github.com/ipython/ipython/issues/819>`_: Adding -m option to %run, similar to -m for python interpreter. |
|
265 | 265 | * `855 <https://github.com/ipython/ipython/issues/855>`_: promote aliases and flags, to ensure they have priority over config files |
|
266 | 266 | * `862 <https://github.com/ipython/ipython/issues/862>`_: BUG: Completion widget position and pager focus. |
|
267 | 267 | * `847 <https://github.com/ipython/ipython/issues/847>`_: Allow connection to kernels by files |
|
268 | 268 | * `708 <https://github.com/ipython/ipython/issues/708>`_: Two-process terminal frontend |
|
269 | 269 | * `857 <https://github.com/ipython/ipython/issues/857>`_: make sdist flags work again (e.g. --manifest-only) |
|
270 | 270 | * `835 <https://github.com/ipython/ipython/issues/835>`_: Add Tab key to list of keys that scroll down the paging widget. |
|
271 | 271 | * `859 <https://github.com/ipython/ipython/issues/859>`_: Fix for issue #800 |
|
272 | 272 | * `848 <https://github.com/ipython/ipython/issues/848>`_: Python3 setup.py install failiure |
|
273 | 273 | * `845 <https://github.com/ipython/ipython/issues/845>`_: Tests on Python 3 |
|
274 | 274 | * `802 <https://github.com/ipython/ipython/issues/802>`_: DOC: extensions: add documentation for the bundled extensions |
|
275 | 275 | * `830 <https://github.com/ipython/ipython/issues/830>`_: contiguous stdout/stderr in notebook |
|
276 | 276 | * `761 <https://github.com/ipython/ipython/issues/761>`_: Windows: test runner fails if repo path (e.g. home dir) contains spaces |
|
277 | 277 | * `801 <https://github.com/ipython/ipython/issues/801>`_: Py3 notebook |
|
278 | 278 | * `809 <https://github.com/ipython/ipython/issues/809>`_: use CFRunLoop directly in `ipython kernel --pylab osx` |
|
279 | 279 | * `841 <https://github.com/ipython/ipython/issues/841>`_: updated old scipy.org links, other minor doc fixes |
|
280 | 280 | * `837 <https://github.com/ipython/ipython/issues/837>`_: remove all trailling spaces |
|
281 | 281 | * `834 <https://github.com/ipython/ipython/issues/834>`_: Issue https://github.com/ipython/ipython/issues/832 resolution |
|
282 | 282 | * `746 <https://github.com/ipython/ipython/issues/746>`_: ENH: extensions: port autoreload to current API |
|
283 | 283 | * `828 <https://github.com/ipython/ipython/issues/828>`_: fixed permissions (sub-modules should not be executable) + added shebang for run_ipy_in_profiler.py |
|
284 | 284 | * `798 <https://github.com/ipython/ipython/issues/798>`_: pexpect & Python 3 |
|
285 | 285 | * `804 <https://github.com/ipython/ipython/issues/804>`_: Magic 'range' crash if greater than len(input_hist) |
|
286 | 286 | * `821 <https://github.com/ipython/ipython/issues/821>`_: update tornado dependency to 2.1 |
|
287 | 287 | * `807 <https://github.com/ipython/ipython/issues/807>`_: Faciliate ssh tunnel sharing by announcing ports |
|
288 | 288 | * `795 <https://github.com/ipython/ipython/issues/795>`_: Add cluster-id for multiple cluster instances per profile |
|
289 | 289 | * `742 <https://github.com/ipython/ipython/issues/742>`_: Glut |
|
290 | 290 | * `668 <https://github.com/ipython/ipython/issues/668>`_: Greedy completer |
|
291 | 291 | * `776 <https://github.com/ipython/ipython/issues/776>`_: Reworking qtconsole shortcut, add fullscreen |
|
292 | 292 | * `790 <https://github.com/ipython/ipython/issues/790>`_: TST: add future unicode_literals test (#786) |
|
293 | 293 | * `775 <https://github.com/ipython/ipython/issues/775>`_: redirect_in/redirect_out should be constrained to windows only |
|
294 | 294 | * `793 <https://github.com/ipython/ipython/issues/793>`_: Don't use readline in the ZMQShell |
|
295 | 295 | * `743 <https://github.com/ipython/ipython/issues/743>`_: Pyglet |
|
296 | 296 | * `774 <https://github.com/ipython/ipython/issues/774>`_: basic/initial .mailmap for nice shortlog summaries |
|
297 | 297 | * `770 <https://github.com/ipython/ipython/issues/770>`_: #769 (reopened) |
|
298 | 298 | * `784 <https://github.com/ipython/ipython/issues/784>`_: Parse user code to AST using compiler flags. |
|
299 | 299 | * `783 <https://github.com/ipython/ipython/issues/783>`_: always use StringIO, never cStringIO |
|
300 | 300 | * `782 <https://github.com/ipython/ipython/issues/782>`_: flush stdout/stderr on displayhook call |
|
301 | 301 | * `622 <https://github.com/ipython/ipython/issues/622>`_: Make pylab import all configurable |
|
302 | 302 | * `745 <https://github.com/ipython/ipython/issues/745>`_: Don't assume history requests succeed in qtconsole |
|
303 | 303 | * `725 <https://github.com/ipython/ipython/issues/725>`_: don't assume cursor.selectedText() is a string |
|
304 | 304 | * `778 <https://github.com/ipython/ipython/issues/778>`_: don't override execfile on Python 2 |
|
305 | 305 | * `663 <https://github.com/ipython/ipython/issues/663>`_: Python 3 compatilibility work |
|
306 | 306 | * `762 <https://github.com/ipython/ipython/issues/762>`_: qtconsole ipython widget's execute_file fails if filename contains spaces or quotes |
|
307 | 307 | * `763 <https://github.com/ipython/ipython/issues/763>`_: Set context for shortcuts in ConsoleWidget |
|
308 | 308 | * `722 <https://github.com/ipython/ipython/issues/722>`_: PyPy compatibility |
|
309 | 309 | * `757 <https://github.com/ipython/ipython/issues/757>`_: ipython.el is broken in 0.11 |
|
310 | 310 | * `764 <https://github.com/ipython/ipython/issues/764>`_: fix "--colors=<color>" option in py-python-command-args. |
|
311 | 311 | * `758 <https://github.com/ipython/ipython/issues/758>`_: use ROUTER/DEALER socket names instead of XREP/XREQ |
|
312 | 312 | * `736 <https://github.com/ipython/ipython/issues/736>`_: enh: added authentication ability for webapp |
|
313 | 313 | * `748 <https://github.com/ipython/ipython/issues/748>`_: Check for tornado before running frontend.html tests. |
|
314 | 314 | * `754 <https://github.com/ipython/ipython/issues/754>`_: restore msg_id/msg_type aliases in top level of msg dict |
|
315 | 315 | * `769 <https://github.com/ipython/ipython/issues/769>`_: Don't treat bytes objects as json-safe |
|
316 | 316 | * `753 <https://github.com/ipython/ipython/issues/753>`_: DOC: msg['msg_type'] removed |
|
317 | 317 | * `766 <https://github.com/ipython/ipython/issues/766>`_: fix "--colors=<color>" option in py-python-command-args. |
|
318 | 318 | * `765 <https://github.com/ipython/ipython/issues/765>`_: fix "--colors=<color>" option in py-python-command-args. |
|
319 | 319 | * `741 <https://github.com/ipython/ipython/issues/741>`_: Run PyOs_InputHook in pager to keep plot windows interactive. |
|
320 | 320 | * `664 <https://github.com/ipython/ipython/issues/664>`_: Remove ipythonrc references from documentation |
|
321 | 321 | * `750 <https://github.com/ipython/ipython/issues/750>`_: Tiny doc fixes |
|
322 | 322 | * `433 <https://github.com/ipython/ipython/issues/433>`_: ZMQ terminal frontend |
|
323 | 323 | * `734 <https://github.com/ipython/ipython/issues/734>`_: Allow %magic argument filenames with spaces to be specified with quotes under win32 |
|
324 | 324 | * `731 <https://github.com/ipython/ipython/issues/731>`_: respect encoding of display data from urls |
|
325 | 325 | * `730 <https://github.com/ipython/ipython/issues/730>`_: doc improvements for running notebook via secure protocol |
|
326 | 326 | * `729 <https://github.com/ipython/ipython/issues/729>`_: use null char to start markdown cell placeholder |
|
327 | 327 | * `727 <https://github.com/ipython/ipython/issues/727>`_: Minor fixes to the htmlnotebook |
|
328 | 328 | * `726 <https://github.com/ipython/ipython/issues/726>`_: use bundled argparse if system argparse is < 1.1 |
|
329 | 329 | * `705 <https://github.com/ipython/ipython/issues/705>`_: Htmlnotebook |
|
330 | 330 | * `723 <https://github.com/ipython/ipython/issues/723>`_: Add 'import time' to IPython/parallel/apps/launcher.py as time.sleep is called without time being imported |
|
331 | 331 | * `714 <https://github.com/ipython/ipython/issues/714>`_: Install mathjax for offline use |
|
332 | 332 | * `718 <https://github.com/ipython/ipython/issues/718>`_: Underline keyboard shortcut characters on appropriate buttons |
|
333 | 333 | * `717 <https://github.com/ipython/ipython/issues/717>`_: Add source highlighting to markdown snippets |
|
334 | 334 | * `716 <https://github.com/ipython/ipython/issues/716>`_: update EvalFormatter to allow arbitrary expressions |
|
335 | 335 | * `712 <https://github.com/ipython/ipython/issues/712>`_: Reset execution counter after cache is cleared |
|
336 | 336 | * `713 <https://github.com/ipython/ipython/issues/713>`_: Align colons in html notebook help dialog |
|
337 | 337 | * `709 <https://github.com/ipython/ipython/issues/709>`_: Allow usage of '.' in notebook names |
|
338 | 338 | * `706 <https://github.com/ipython/ipython/issues/706>`_: Implement static publishing of HTML notebook |
|
339 | 339 | * `674 <https://github.com/ipython/ipython/issues/674>`_: use argparse to parse aliases & flags |
|
340 | 340 | * `679 <https://github.com/ipython/ipython/issues/679>`_: HistoryManager.get_session_info() |
|
341 | 341 | * `696 <https://github.com/ipython/ipython/issues/696>`_: Fix columnize bug, where tab completion with very long filenames would crash Qt console |
|
342 | 342 | * `686 <https://github.com/ipython/ipython/issues/686>`_: add ssh tunnel support to qtconsole |
|
343 | 343 | * `685 <https://github.com/ipython/ipython/issues/685>`_: Add SSH tunneling to engines |
|
344 | 344 | * `384 <https://github.com/ipython/ipython/issues/384>`_: Allow pickling objects defined interactively. |
|
345 | 345 | * `647 <https://github.com/ipython/ipython/issues/647>`_: My fix rpmlint |
|
346 | 346 | * `587 <https://github.com/ipython/ipython/issues/587>`_: don't special case for py3k+numpy |
|
347 | 347 | * `703 <https://github.com/ipython/ipython/issues/703>`_: make config-loading debug messages more explicit |
|
348 | 348 | * `699 <https://github.com/ipython/ipython/issues/699>`_: make calltips configurable in qtconsole |
|
349 | 349 | * `666 <https://github.com/ipython/ipython/issues/666>`_: parallel tests & extra readline escapes |
|
350 | 350 | * `683 <https://github.com/ipython/ipython/issues/683>`_: BF - allow nose with-doctest setting in environment |
|
351 | 351 | * `689 <https://github.com/ipython/ipython/issues/689>`_: Protect ipkernel from bad messages |
|
352 | 352 | * `702 <https://github.com/ipython/ipython/issues/702>`_: Prevent ipython.py launcher from being imported. |
|
353 | 353 | * `701 <https://github.com/ipython/ipython/issues/701>`_: Prevent ipython.py from being imported by accident |
|
354 | 354 | * `670 <https://github.com/ipython/ipython/issues/670>`_: check for writable dirs, not just existence, in utils.path |
|
355 | 355 | * `579 <https://github.com/ipython/ipython/issues/579>`_: Sessionwork |
|
356 | 356 | * `687 <https://github.com/ipython/ipython/issues/687>`_: add `ipython kernel` for starting just a kernel |
|
357 | 357 | * `627 <https://github.com/ipython/ipython/issues/627>`_: Qt Console history search |
|
358 | 358 | * `646 <https://github.com/ipython/ipython/issues/646>`_: Generate package list automatically in find_packages |
|
359 | 359 | * `660 <https://github.com/ipython/ipython/issues/660>`_: i658 |
|
360 | 360 | * `659 <https://github.com/ipython/ipython/issues/659>`_: don't crash on bad config files |
|
361 | 361 | |
|
362 | 362 | Regular issues (258): |
|
363 | 363 | |
|
364 | 364 | * `1177 <https://github.com/ipython/ipython/issues/1177>`_: UnicodeDecodeError in py3compat from "xlrd??" |
|
365 | 365 | * `1094 <https://github.com/ipython/ipython/issues/1094>`_: Tooltip doesn't show constructor docstrings |
|
366 | 366 | * `1170 <https://github.com/ipython/ipython/issues/1170>`_: double pylab greeting with c.InteractiveShellApp.pylab = "tk" in zmqconsole |
|
367 | 367 | * `1166 <https://github.com/ipython/ipython/issues/1166>`_: E-mail cpaste broken |
|
368 | 368 | * `1164 <https://github.com/ipython/ipython/issues/1164>`_: IPython qtconsole (0.12) can't export to html with external png |
|
369 | 369 | * `1103 <https://github.com/ipython/ipython/issues/1103>`_: %loadpy should cut out encoding declaration |
|
370 | 370 | * `1156 <https://github.com/ipython/ipython/issues/1156>`_: Notebooks downloaded as Python files require a header stating the encoding |
|
371 | 371 | * `1157 <https://github.com/ipython/ipython/issues/1157>`_: Ctrl-C not working when GUI/pylab integration is active |
|
372 | 372 | * `1154 <https://github.com/ipython/ipython/issues/1154>`_: We should be less aggressive in de-registering post-execution functions |
|
373 | 373 | * `1134 <https://github.com/ipython/ipython/issues/1134>`_: "select-all, kill" leaves qtconsole in unusable state |
|
374 | 374 | * `1148 <https://github.com/ipython/ipython/issues/1148>`_: A lot of testerrors |
|
375 | 375 | * `803 <https://github.com/ipython/ipython/issues/803>`_: Make doctests work with Python 3 |
|
376 | 376 | * `1119 <https://github.com/ipython/ipython/issues/1119>`_: Start menu shortcuts not created in Python 3 |
|
377 | 377 | * `1136 <https://github.com/ipython/ipython/issues/1136>`_: The embedding machinery ignores user_ns |
|
378 | 378 | * `607 <https://github.com/ipython/ipython/issues/607>`_: Use the new IPython logo/font in the notebook header |
|
379 | 379 | * `755 <https://github.com/ipython/ipython/issues/755>`_: qtconsole ipython widget's execute_file fails if filename contains spaces or quotes |
|
380 | 380 | * `1115 <https://github.com/ipython/ipython/issues/1115>`_: shlex_split should return unicode |
|
381 | 381 | * `1109 <https://github.com/ipython/ipython/issues/1109>`_: timeit with string ending in space gives "ValueError: No closing quotation" |
|
382 | 382 | * `1142 <https://github.com/ipython/ipython/issues/1142>`_: Install problems |
|
383 | 383 | * `700 <https://github.com/ipython/ipython/issues/700>`_: Some SVG images render incorrectly in htmlnotebook |
|
384 | 384 | * `1117 <https://github.com/ipython/ipython/issues/1117>`_: quit() doesn't work in terminal |
|
385 | 385 | * `1111 <https://github.com/ipython/ipython/issues/1111>`_: ls broken after merge of #1089 |
|
386 | 386 | * `1104 <https://github.com/ipython/ipython/issues/1104>`_: Prompt spacing weird |
|
387 | 387 | * `1124 <https://github.com/ipython/ipython/issues/1124>`_: Seg Fault 11 when calling PySide using "run" command |
|
388 | 388 | * `1088 <https://github.com/ipython/ipython/issues/1088>`_: QtConsole : can't copy from pager |
|
389 | 389 | * `568 <https://github.com/ipython/ipython/issues/568>`_: Test error and failure in IPython.core on windows |
|
390 | 390 | * `1112 <https://github.com/ipython/ipython/issues/1112>`_: testfailure in IPython.frontend on windows |
|
391 | 391 | * `1102 <https://github.com/ipython/ipython/issues/1102>`_: magic in IPythonDemo fails when not located at top of demo file |
|
392 | 392 | * `629 <https://github.com/ipython/ipython/issues/629>`_: \r and \b in qtconsole don't behave as expected |
|
393 | 393 | * `1080 <https://github.com/ipython/ipython/issues/1080>`_: Notebook: tab completion should close on "(" |
|
394 | 394 | * `973 <https://github.com/ipython/ipython/issues/973>`_: Qt Console close dialog and on-top Qt Console |
|
395 | 395 | * `1087 <https://github.com/ipython/ipython/issues/1087>`_: QtConsole xhtml/Svg export broken ? |
|
396 | 396 | * `1067 <https://github.com/ipython/ipython/issues/1067>`_: Parallel test suite hangs on Python 3 |
|
397 | 397 | * `1018 <https://github.com/ipython/ipython/issues/1018>`_: Local mathjax breaks install |
|
398 | 398 | * `993 <https://github.com/ipython/ipython/issues/993>`_: `raw_input` redirection to foreign kernels is extremely brittle |
|
399 | 399 | * `1100 <https://github.com/ipython/ipython/issues/1100>`_: ipython3 traceback unicode issue from extensions |
|
400 | 400 | * `1071 <https://github.com/ipython/ipython/issues/1071>`_: Large html-notebooks hang on load on a slow machine |
|
401 | 401 | * `89 <https://github.com/ipython/ipython/issues/89>`_: %pdoc np.ma.compress shows docstring twice |
|
402 | 402 | * `22 <https://github.com/ipython/ipython/issues/22>`_: Include improvements from anythingipython.el |
|
403 | 403 | * `633 <https://github.com/ipython/ipython/issues/633>`_: Execution count & SyntaxError |
|
404 | 404 | * `1095 <https://github.com/ipython/ipython/issues/1095>`_: Uncaught TypeError: Object has no method 'remove_and_cancell_tooltip' |
|
405 | 405 | * `1075 <https://github.com/ipython/ipython/issues/1075>`_: We're ignoring prompt customizations |
|
406 | 406 | * `1086 <https://github.com/ipython/ipython/issues/1086>`_: Can't open qtconsole from outside source tree |
|
407 | 407 | * `1076 <https://github.com/ipython/ipython/issues/1076>`_: namespace changes broke `foo.*bar*?` syntax |
|
408 | 408 | * `1074 <https://github.com/ipython/ipython/issues/1074>`_: pprinting old-style class objects fails (TypeError: 'tuple' object is not callable) |
|
409 | 409 | * `1063 <https://github.com/ipython/ipython/issues/1063>`_: IPython.utils test error due to missing unicodedata module |
|
410 | 410 | * `592 <https://github.com/ipython/ipython/issues/592>`_: Bug in argument parsing for %run |
|
411 | 411 | * `378 <https://github.com/ipython/ipython/issues/378>`_: Windows path escape issues |
|
412 | 412 | * `1068 <https://github.com/ipython/ipython/issues/1068>`_: Notebook tab completion broken in Firefox |
|
413 | 413 | * `75 <https://github.com/ipython/ipython/issues/75>`_: No tab completion after "/ |
|
414 | 414 | * `103 <https://github.com/ipython/ipython/issues/103>`_: customizable cpaste |
|
415 | 415 | * `324 <https://github.com/ipython/ipython/issues/324>`_: Remove code in IPython.testing that is not being used |
|
416 | 416 | * `131 <https://github.com/ipython/ipython/issues/131>`_: Global variables not seen by cprofile.run() |
|
417 | 417 | * `851 <https://github.com/ipython/ipython/issues/851>`_: IPython shell swallows exceptions in certain circumstances |
|
418 | 418 | * `882 <https://github.com/ipython/ipython/issues/882>`_: ipython freezes at start if IPYTHONDIR is on an NFS mount |
|
419 | 419 | * `1057 <https://github.com/ipython/ipython/issues/1057>`_: Blocker: Qt console broken after "all magics" menu became dynamic |
|
420 | 420 | * `1027 <https://github.com/ipython/ipython/issues/1027>`_: ipython does not like white space at end of file |
|
421 | 421 | * `1058 <https://github.com/ipython/ipython/issues/1058>`_: New bug: Notebook asks for confirmation to leave even saved pages. |
|
422 | 422 | * `1061 <https://github.com/ipython/ipython/issues/1061>`_: rep (magic recall) under pypy |
|
423 | 423 | * `1047 <https://github.com/ipython/ipython/issues/1047>`_: Document the notebook format |
|
424 | 424 | * `102 <https://github.com/ipython/ipython/issues/102>`_: Properties accessed twice for classes defined interactively |
|
425 | 425 | * `16 <https://github.com/ipython/ipython/issues/16>`_: %store raises exception when storing compiled regex |
|
426 | 426 | * `67 <https://github.com/ipython/ipython/issues/67>`_: tab expansion should only take one directory level at the time |
|
427 | 427 | * `62 <https://github.com/ipython/ipython/issues/62>`_: Global variables undefined in interactive use of embedded ipython shell |
|
428 | 428 | * `57 <https://github.com/ipython/ipython/issues/57>`_: debugging with ipython does not work well outside ipython |
|
429 | 429 | * `38 <https://github.com/ipython/ipython/issues/38>`_: Line entry edge case error |
|
430 | 430 | * `980 <https://github.com/ipython/ipython/issues/980>`_: Update parallel docs for new parallel architecture |
|
431 | 431 | * `1017 <https://github.com/ipython/ipython/issues/1017>`_: Add small example about ipcluster/ssh startup |
|
432 | 432 | * `1041 <https://github.com/ipython/ipython/issues/1041>`_: Proxy Issues |
|
433 | 433 | * `967 <https://github.com/ipython/ipython/issues/967>`_: KernelManagers don't use zmq eventloop properly |
|
434 | 434 | * `1055 <https://github.com/ipython/ipython/issues/1055>`_: "All Magics" display on Ubuntu |
|
435 | 435 | * `1054 <https://github.com/ipython/ipython/issues/1054>`_: ipython explodes on syntax error |
|
436 | 436 | * `1051 <https://github.com/ipython/ipython/issues/1051>`_: ipython3 set_next_input() failure |
|
437 | 437 | * `693 <https://github.com/ipython/ipython/issues/693>`_: "run -i" no longer works after %reset in terminal |
|
438 | 438 | * `29 <https://github.com/ipython/ipython/issues/29>`_: cPickle works in standard interpreter, but not in IPython |
|
439 | 439 | * `1050 <https://github.com/ipython/ipython/issues/1050>`_: ipython3 broken by commit 8bb887c8c2c447bf7 |
|
440 | 440 | * `1048 <https://github.com/ipython/ipython/issues/1048>`_: Update docs on notebook password |
|
441 | 441 | * `1046 <https://github.com/ipython/ipython/issues/1046>`_: Searies of questions/issues? |
|
442 | 442 | * `1045 <https://github.com/ipython/ipython/issues/1045>`_: crash when exiting - previously launched embedded sub-shell |
|
443 | 443 | * `1043 <https://github.com/ipython/ipython/issues/1043>`_: pylab doesn't work in qtconsole |
|
444 | 444 | * `1044 <https://github.com/ipython/ipython/issues/1044>`_: run -p doesn't work in python 3 |
|
445 | 445 | * `1010 <https://github.com/ipython/ipython/issues/1010>`_: emacs freezes when ipython-complete is called |
|
446 | 446 | * `82 <https://github.com/ipython/ipython/issues/82>`_: Update devel docs with discussion about good changelogs |
|
447 | 447 | * `116 <https://github.com/ipython/ipython/issues/116>`_: Update release management scipts and release.revision for git |
|
448 | 448 | * `1022 <https://github.com/ipython/ipython/issues/1022>`_: Pylab banner shows up with first cell to execute |
|
449 | 449 | * `787 <https://github.com/ipython/ipython/issues/787>`_: Keyboard selection of multiple lines in the notebook behaves inconsistently |
|
450 | 450 | * `1037 <https://github.com/ipython/ipython/issues/1037>`_: notepad + jsonlib: TypeError: Only whitespace may be used for indentation. |
|
451 | 451 | * `970 <https://github.com/ipython/ipython/issues/970>`_: Default home not writable, %HOME% does not help (windows) |
|
452 | 452 | * `747 <https://github.com/ipython/ipython/issues/747>`_: HOMESHARE not a good choice for "writable homedir" on Windows |
|
453 | 453 | * `810 <https://github.com/ipython/ipython/issues/810>`_: cleanup utils.path.get_home_dir |
|
454 | 454 | * `2 <https://github.com/ipython/ipython/issues/2>`_: Fix the copyright statement in source code files to be accurate |
|
455 | 455 | * `1031 <https://github.com/ipython/ipython/issues/1031>`_: <esc> on Firefox crash websocket |
|
456 | 456 | * `684 <https://github.com/ipython/ipython/issues/684>`_: %Store eliminated in configuration and magic commands in 0.11 |
|
457 | 457 | * `1026 <https://github.com/ipython/ipython/issues/1026>`_: BUG: wrong default parameter in ask_yes_no |
|
458 | 458 | * `880 <https://github.com/ipython/ipython/issues/880>`_: Better error message if %paste fails |
|
459 | 459 | * `1024 <https://github.com/ipython/ipython/issues/1024>`_: autopx magic broken |
|
460 | 460 | * `822 <https://github.com/ipython/ipython/issues/822>`_: Unicode bug in Itpl when expanding shell variables in syscalls with ! |
|
461 | 461 | * `1009 <https://github.com/ipython/ipython/issues/1009>`_: Windows: regression in cd magic handling of paths |
|
462 | 462 | * `833 <https://github.com/ipython/ipython/issues/833>`_: Crash python with matplotlib and unequal length arrays |
|
463 | 463 | * `695 <https://github.com/ipython/ipython/issues/695>`_: Crash handler initialization is too aggressive |
|
464 | 464 | * `1000 <https://github.com/ipython/ipython/issues/1000>`_: Remove duplicates when refilling readline history |
|
465 | 465 | * `992 <https://github.com/ipython/ipython/issues/992>`_: Interrupting certain matplotlib operations leaves the inline backend 'wedged' |
|
466 | 466 | * `942 <https://github.com/ipython/ipython/issues/942>`_: number traits should cast if value doesn't change |
|
467 | 467 | * `1006 <https://github.com/ipython/ipython/issues/1006>`_: ls crashes when run on a UNC path or with non-ascii args |
|
468 | 468 | * `944 <https://github.com/ipython/ipython/issues/944>`_: Decide the default image format for inline figures: SVG or PNG? |
|
469 | 469 | * `842 <https://github.com/ipython/ipython/issues/842>`_: Python 3 on Windows (pyreadline) - expected an object with the buffer interface |
|
470 | 470 | * `1002 <https://github.com/ipython/ipython/issues/1002>`_: ImportError due to incorrect version checking |
|
471 | 471 | * `1001 <https://github.com/ipython/ipython/issues/1001>`_: Ipython "source" command? |
|
472 | 472 | * `954 <https://github.com/ipython/ipython/issues/954>`_: IPython embed doesn't respect namespaces |
|
473 | 473 | * `681 <https://github.com/ipython/ipython/issues/681>`_: pdb freezes inside qtconsole |
|
474 | 474 | * `698 <https://github.com/ipython/ipython/issues/698>`_: crash report "TypeError: can only concatenate list (not "unicode") to list" |
|
475 | 475 | * `978 <https://github.com/ipython/ipython/issues/978>`_: ipython 0.11 buffers external command output till the cmd is done |
|
476 | 476 | * `952 <https://github.com/ipython/ipython/issues/952>`_: Need user-facing warning in the browser if websocket connection fails |
|
477 | 477 | * `988 <https://github.com/ipython/ipython/issues/988>`_: Error using idlsave |
|
478 | 478 | * `990 <https://github.com/ipython/ipython/issues/990>`_: ipython notebook - kernel dies if matplotlib is not installed |
|
479 | 479 | * `752 <https://github.com/ipython/ipython/issues/752>`_: Matplotlib figures showed only once in notebook |
|
480 | 480 | * `54 <https://github.com/ipython/ipython/issues/54>`_: Exception hook should be optional for embedding IPython in GUIs |
|
481 | 481 | * `918 <https://github.com/ipython/ipython/issues/918>`_: IPython.frontend tests fail without tornado |
|
482 | 482 | * `986 <https://github.com/ipython/ipython/issues/986>`_: Views created with c.direct_view() fail |
|
483 | 483 | * `697 <https://github.com/ipython/ipython/issues/697>`_: Filter out from %who names loaded at initialization time |
|
484 | 484 | * `932 <https://github.com/ipython/ipython/issues/932>`_: IPython 0.11 quickref card has superfluous "%recall and" |
|
485 | 485 | * `982 <https://github.com/ipython/ipython/issues/982>`_: png files with executable permissions |
|
486 | 486 | * `914 <https://github.com/ipython/ipython/issues/914>`_: Simpler system for running code after InteractiveShell is initialised |
|
487 | 487 | * `911 <https://github.com/ipython/ipython/issues/911>`_: ipython crashes on startup if readline is missing |
|
488 | 488 | * `971 <https://github.com/ipython/ipython/issues/971>`_: bookmarks created in 0.11 are corrupt in 0.12 |
|
489 | 489 | * `974 <https://github.com/ipython/ipython/issues/974>`_: object feature tab-completion crash |
|
490 | 490 | * `939 <https://github.com/ipython/ipython/issues/939>`_: ZMQShell always uses default profile |
|
491 | 491 | * `946 <https://github.com/ipython/ipython/issues/946>`_: Multi-tab Close action should offer option to leave all kernels alone |
|
492 | 492 | * `949 <https://github.com/ipython/ipython/issues/949>`_: Test suite must not require any manual interaction |
|
493 | 493 | * `643 <https://github.com/ipython/ipython/issues/643>`_: enable gui eventloop integration in ipkernel |
|
494 | 494 | * `965 <https://github.com/ipython/ipython/issues/965>`_: ipython is crashed without launch.(python3.2) |
|
495 | 495 | * `958 <https://github.com/ipython/ipython/issues/958>`_: Can't use os X clipboard on with qtconsole |
|
496 | 496 | * `962 <https://github.com/ipython/ipython/issues/962>`_: Don't require tornado in the tests |
|
497 | 497 | * `960 <https://github.com/ipython/ipython/issues/960>`_: crash on syntax error on Windows XP |
|
498 | 498 | * `934 <https://github.com/ipython/ipython/issues/934>`_: The latest ipython branch doesn't work in Chrome |
|
499 | 499 | * `870 <https://github.com/ipython/ipython/issues/870>`_: zmq version detection |
|
500 | 500 | * `943 <https://github.com/ipython/ipython/issues/943>`_: HISTIGNORE for IPython |
|
501 | 501 | * `947 <https://github.com/ipython/ipython/issues/947>`_: qtconsole segfaults at startup |
|
502 | 502 | * `903 <https://github.com/ipython/ipython/issues/903>`_: Expose a magic to control config of the inline pylab backend |
|
503 | 503 | * `908 <https://github.com/ipython/ipython/issues/908>`_: bad user config shouldn't crash IPython |
|
504 | 504 | * `935 <https://github.com/ipython/ipython/issues/935>`_: Typing `break` causes IPython to crash. |
|
505 | 505 | * `869 <https://github.com/ipython/ipython/issues/869>`_: Tab completion of `~/` shows no output post 0.10.x |
|
506 | 506 | * `904 <https://github.com/ipython/ipython/issues/904>`_: whos under pypy1.6 |
|
507 | 507 | * `773 <https://github.com/ipython/ipython/issues/773>`_: check_security_dir() and check_pid_dir() fail on network filesystem |
|
508 | 508 | * `915 <https://github.com/ipython/ipython/issues/915>`_: OS X Lion Terminal.app line wrap problem |
|
509 | 509 | * `886 <https://github.com/ipython/ipython/issues/886>`_: Notebook kernel crash when specifying --notebook-dir on commandline |
|
510 | 510 | * `636 <https://github.com/ipython/ipython/issues/636>`_: debugger.py: pydb broken |
|
511 | 511 | * `808 <https://github.com/ipython/ipython/issues/808>`_: Ctrl+C during %reset confirm message crash Qtconsole |
|
512 | 512 | * `927 <https://github.com/ipython/ipython/issues/927>`_: Using return outside a function crashes ipython |
|
513 | 513 | * `919 <https://github.com/ipython/ipython/issues/919>`_: Pop-up segfault when moving cursor out of qtconsole window |
|
514 | 514 | * `181 <https://github.com/ipython/ipython/issues/181>`_: cls command does not work on windows |
|
515 | 515 | * `917 <https://github.com/ipython/ipython/issues/917>`_: documentation typos |
|
516 | 516 | * `818 <https://github.com/ipython/ipython/issues/818>`_: %run does not work with non-ascii characeters in path |
|
517 | 517 | * `907 <https://github.com/ipython/ipython/issues/907>`_: Errors in custom completer functions can crash IPython |
|
518 | 518 | * `867 <https://github.com/ipython/ipython/issues/867>`_: doc: notebook password authentication howto |
|
519 | 519 | * `211 <https://github.com/ipython/ipython/issues/211>`_: paste command not working |
|
520 | 520 | * `900 <https://github.com/ipython/ipython/issues/900>`_: Tab key should insert 4 spaces in qt console |
|
521 | 521 | * `513 <https://github.com/ipython/ipython/issues/513>`_: [Qt console] cannot insert new lines into console functions using tab |
|
522 | 522 | * `906 <https://github.com/ipython/ipython/issues/906>`_: qtconsoleapp 'parse_command_line' doen't like --existing anymore |
|
523 | 523 | * `638 <https://github.com/ipython/ipython/issues/638>`_: Qt console --pylab=inline and getfigs(), etc. |
|
524 | 524 | * `710 <https://github.com/ipython/ipython/issues/710>`_: unwanted unicode passed to args |
|
525 | 525 | * `436 <https://github.com/ipython/ipython/issues/436>`_: Users should see tooltips for all buttons in the notebook UI |
|
526 | 526 | * `207 <https://github.com/ipython/ipython/issues/207>`_: ipython crashes if atexit handler raises exception |
|
527 | 527 | * `692 <https://github.com/ipython/ipython/issues/692>`_: use of Tracer() when debugging works but gives error messages |
|
528 | 528 | * `690 <https://github.com/ipython/ipython/issues/690>`_: debugger does not print error message by default in 0.11 |
|
529 | 529 | * `571 <https://github.com/ipython/ipython/issues/571>`_: history of multiline entries |
|
530 | 530 | * `749 <https://github.com/ipython/ipython/issues/749>`_: IPython.parallel test failure under Windows 7 and XP |
|
531 | 531 | * `890 <https://github.com/ipython/ipython/issues/890>`_: ipclusterapp.py - helep |
|
532 | 532 | * `885 <https://github.com/ipython/ipython/issues/885>`_: `ws-hostname` alias not recognized by notebook |
|
533 | 533 | * `881 <https://github.com/ipython/ipython/issues/881>`_: Missing manual.pdf? |
|
534 | 534 | * `744 <https://github.com/ipython/ipython/issues/744>`_: cannot create notebook in offline mode if mathjax not installed |
|
535 | 535 | * `865 <https://github.com/ipython/ipython/issues/865>`_: Make tracebacks from %paste show the code |
|
536 | 536 | * `535 <https://github.com/ipython/ipython/issues/535>`_: exception unicode handling in %run is faulty in qtconsole |
|
537 | 537 | * `817 <https://github.com/ipython/ipython/issues/817>`_: iPython crashed |
|
538 | 538 | * `799 <https://github.com/ipython/ipython/issues/799>`_: %edit magic not working on windows xp in qtconsole |
|
539 | 539 | * `732 <https://github.com/ipython/ipython/issues/732>`_: QTConsole wrongly promotes the index of the input line on which user presses Enter |
|
540 | 540 | * `662 <https://github.com/ipython/ipython/issues/662>`_: ipython test failures on Mac OS X Lion |
|
541 | 541 | * `650 <https://github.com/ipython/ipython/issues/650>`_: Handle bad config files better |
|
542 | 542 | * `829 <https://github.com/ipython/ipython/issues/829>`_: We should not insert new lines after all print statements in the notebook |
|
543 | 543 | * `874 <https://github.com/ipython/ipython/issues/874>`_: ipython-qtconsole: pyzmq Version Comparison |
|
544 | 544 | * `640 <https://github.com/ipython/ipython/issues/640>`_: matplotlib macosx windows don't respond in qtconsole |
|
545 | 545 | * `624 <https://github.com/ipython/ipython/issues/624>`_: ipython intermittently segfaults when figure is closed (Mac OS X) |
|
546 | 546 | * `871 <https://github.com/ipython/ipython/issues/871>`_: Notebook crashes if a profile is used |
|
547 | 547 | * `56 <https://github.com/ipython/ipython/issues/56>`_: Have %cpaste accept also Ctrl-D as a termination marker |
|
548 | 548 | * `849 <https://github.com/ipython/ipython/issues/849>`_: Command line options to not override profile options |
|
549 | 549 | * `806 <https://github.com/ipython/ipython/issues/806>`_: Provide single-port connection to kernels |
|
550 | 550 | * `691 <https://github.com/ipython/ipython/issues/691>`_: [wishlist] Automatically find existing kernel |
|
551 | 551 | * `688 <https://github.com/ipython/ipython/issues/688>`_: local security vulnerability: all ports visible to any local user. |
|
552 | 552 | * `866 <https://github.com/ipython/ipython/issues/866>`_: DistributionNotFound on running ipython 0.11 on Windows XP x86 |
|
553 | 553 | * `673 <https://github.com/ipython/ipython/issues/673>`_: raw_input appears to be round-robin for qtconsole |
|
554 | 554 | * `863 <https://github.com/ipython/ipython/issues/863>`_: Graceful degradation when home directory not writable |
|
555 | 555 | * `800 <https://github.com/ipython/ipython/issues/800>`_: Timing scripts with run -t -N <N> fails on report output |
|
556 | 556 | * `858 <https://github.com/ipython/ipython/issues/858>`_: Typing 'continue' makes ipython0.11 crash |
|
557 | 557 | * `840 <https://github.com/ipython/ipython/issues/840>`_: all processes run on one CPU core |
|
558 | 558 | * `843 <https://github.com/ipython/ipython/issues/843>`_: "import braces" crashes ipython |
|
559 | 559 | * `836 <https://github.com/ipython/ipython/issues/836>`_: Strange Output after IPython Install |
|
560 | 560 | * `839 <https://github.com/ipython/ipython/issues/839>`_: Qtconsole segfaults when mouse exits window with active tooltip |
|
561 | 561 | * `827 <https://github.com/ipython/ipython/issues/827>`_: Add support for checking several limits before running task on engine |
|
562 | 562 | * `826 <https://github.com/ipython/ipython/issues/826>`_: Add support for creation of parallel task when no engine is running |
|
563 | 563 | * `832 <https://github.com/ipython/ipython/issues/832>`_: Improve error message for %logstop |
|
564 | 564 | * `831 <https://github.com/ipython/ipython/issues/831>`_: %logstart in read-only directory forbid any further command |
|
565 | 565 | * `814 <https://github.com/ipython/ipython/issues/814>`_: ipython does not start -- DistributionNotFound |
|
566 | 566 | * `794 <https://github.com/ipython/ipython/issues/794>`_: Allow >1 controller per profile |
|
567 | 567 | * `820 <https://github.com/ipython/ipython/issues/820>`_: Tab Completion feature |
|
568 | 568 | * `812 <https://github.com/ipython/ipython/issues/812>`_: Qt console crashes on Ubuntu 11.10 |
|
569 | 569 | * `816 <https://github.com/ipython/ipython/issues/816>`_: Import error using Python 2.7 and dateutil2.0 No module named _thread |
|
570 | 570 | * `756 <https://github.com/ipython/ipython/issues/756>`_: qtconsole Windows fails to print error message for '%run nonexistent_file' |
|
571 | 571 | * `651 <https://github.com/ipython/ipython/issues/651>`_: Completion doesn't work on element of a list |
|
572 | 572 | * `617 <https://github.com/ipython/ipython/issues/617>`_: [qtconsole] %hist doesn't show anything in qtconsole |
|
573 | 573 | * `786 <https://github.com/ipython/ipython/issues/786>`_: from __future__ import unicode_literals does not work |
|
574 | 574 | * `779 <https://github.com/ipython/ipython/issues/779>`_: Using irunner from virtual evn uses systemwide ipython |
|
575 | 575 | * `768 <https://github.com/ipython/ipython/issues/768>`_: codepage handling of output from scripts and shellcommands are not handled properly by qtconsole |
|
576 | 576 | * `785 <https://github.com/ipython/ipython/issues/785>`_: Don't strip leading whitespace in repr() in notebook |
|
577 | 577 | * `737 <https://github.com/ipython/ipython/issues/737>`_: in pickleshare.py line52 should be "if not os.path.isdir(self.root):"? |
|
578 | 578 | * `738 <https://github.com/ipython/ipython/issues/738>`_: in ipthon_win_post_install.py line 38 |
|
579 | 579 | * `777 <https://github.com/ipython/ipython/issues/777>`_: print(β¦, sep=β¦) raises SyntaxError |
|
580 | 580 | * `728 <https://github.com/ipython/ipython/issues/728>`_: ipcontroller crash with MPI |
|
581 | 581 | * `780 <https://github.com/ipython/ipython/issues/780>`_: qtconsole Out value prints before the print statements that precede it |
|
582 | 582 | * `632 <https://github.com/ipython/ipython/issues/632>`_: IPython Crash Report (0.10.2) |
|
583 | 583 | * `253 <https://github.com/ipython/ipython/issues/253>`_: Unable to install ipython on windows |
|
584 | 584 | * `80 <https://github.com/ipython/ipython/issues/80>`_: Split IPClusterApp into multiple Application subclasses for each subcommand |
|
585 | 585 | * `34 <https://github.com/ipython/ipython/issues/34>`_: non-blocking pendingResult partial results |
|
586 | 586 | * `739 <https://github.com/ipython/ipython/issues/739>`_: Tests fail if tornado not installed |
|
587 | 587 | * `719 <https://github.com/ipython/ipython/issues/719>`_: Better support Pypy |
|
588 | 588 | * `667 <https://github.com/ipython/ipython/issues/667>`_: qtconsole problem with default pylab profile |
|
589 | 589 | * `661 <https://github.com/ipython/ipython/issues/661>`_: ipythonrc referenced in magic command in 0.11 |
|
590 | 590 | * `665 <https://github.com/ipython/ipython/issues/665>`_: Source introspection with ?? is broken |
|
591 | 591 | * `724 <https://github.com/ipython/ipython/issues/724>`_: crash - ipython qtconsole, %quickref |
|
592 | 592 | * `655 <https://github.com/ipython/ipython/issues/655>`_: ipython with qtconsole crashes |
|
593 | 593 | * `593 <https://github.com/ipython/ipython/issues/593>`_: HTML Notebook Prompt can be deleted . . . |
|
594 | 594 | * `563 <https://github.com/ipython/ipython/issues/563>`_: use argparse instead of kvloader for flags&aliases |
|
595 | 595 | * `751 <https://github.com/ipython/ipython/issues/751>`_: Tornado version greater than 2.0 needed for firefox 6 |
|
596 | 596 | * `720 <https://github.com/ipython/ipython/issues/720>`_: Crash report when importing easter egg |
|
597 | 597 | * `740 <https://github.com/ipython/ipython/issues/740>`_: Ctrl-Enter clears line in notebook |
|
598 | 598 | * `772 <https://github.com/ipython/ipython/issues/772>`_: ipengine fails on Windows with "XXX lineno: 355, opcode: 0" |
|
599 | 599 | * `771 <https://github.com/ipython/ipython/issues/771>`_: Add python 3 tag to setup.py |
|
600 | 600 | * `767 <https://github.com/ipython/ipython/issues/767>`_: non-ascii in __doc__ string crashes qtconsole kernel when showing tooltip |
|
601 | 601 | * `733 <https://github.com/ipython/ipython/issues/733>`_: In Windows, %run fails to strip quotes from filename |
|
602 | 602 | * `721 <https://github.com/ipython/ipython/issues/721>`_: no completion in emacs by ipython(ipython.el) |
|
603 | 603 | * `669 <https://github.com/ipython/ipython/issues/669>`_: Do not accept an ipython_dir that's not writeable |
|
604 | 604 | * `711 <https://github.com/ipython/ipython/issues/711>`_: segfault on mac os x |
|
605 | 605 | * `500 <https://github.com/ipython/ipython/issues/500>`_: "RuntimeError: Cannot change input buffer during execution" in console_widget.py |
|
606 | 606 | * `707 <https://github.com/ipython/ipython/issues/707>`_: Copy and paste keyboard shortcuts do not work in Qt Console on OS X |
|
607 | 607 | * `478 <https://github.com/ipython/ipython/issues/478>`_: PyZMQ's use of memoryviews breaks reconstruction of numpy arrays |
|
608 | 608 | * `694 <https://github.com/ipython/ipython/issues/694>`_: Turning off callout tips in qtconsole |
|
609 | 609 | * `704 <https://github.com/ipython/ipython/issues/704>`_: return kills IPython |
|
610 | 610 | * `442 <https://github.com/ipython/ipython/issues/442>`_: Users should have intelligent autoindenting in the notebook |
|
611 | 611 | * `615 <https://github.com/ipython/ipython/issues/615>`_: Wireframe and implement a project dashboard page |
|
612 | 612 | * `614 <https://github.com/ipython/ipython/issues/614>`_: Wireframe and implement a notebook dashboard page |
|
613 | 613 | * `606 <https://github.com/ipython/ipython/issues/606>`_: Users should be able to use the notebook to import/export a notebook to .py or .rst |
|
614 | 614 | * `604 <https://github.com/ipython/ipython/issues/604>`_: A user should be able to leave a kernel running in the notebook and reconnect |
|
615 | 615 | * `298 <https://github.com/ipython/ipython/issues/298>`_: Users should be able to save a notebook and then later reload it |
|
616 | 616 | * `649 <https://github.com/ipython/ipython/issues/649>`_: ipython qtconsole (v0.11): setting "c.IPythonWidget.in_prompt = '>>> ' crashes |
|
617 | 617 | * `672 <https://github.com/ipython/ipython/issues/672>`_: What happened to Exit? |
|
618 | 618 | * `658 <https://github.com/ipython/ipython/issues/658>`_: Put the InteractiveShellApp section first in the auto-generated config files |
|
619 | 619 | * `656 <https://github.com/ipython/ipython/issues/656>`_: [suggestion] dependency checking for pyqt for Windows installer |
|
620 | 620 | * `654 <https://github.com/ipython/ipython/issues/654>`_: broken documentation link on download page |
|
621 | 621 | * `653 <https://github.com/ipython/ipython/issues/653>`_: Test failures in IPython.parallel |
@@ -1,1784 +1,1784 b'' | |||
|
1 | 1 | .. _issues_list_100: |
|
2 | 2 | |
|
3 | 3 | Issues closed in the 1.0 development cycle |
|
4 | 4 | ========================================== |
|
5 | 5 | |
|
6 | 6 | Issues closed in 1.1 |
|
7 | 7 | -------------------- |
|
8 | 8 | |
|
9 | 9 | GitHub stats for 2013/08/08 - 2013/09/09 (since 1.0) |
|
10 | 10 | |
|
11 | 11 | These lists are automatically generated, and may be incomplete or contain duplicates. |
|
12 | 12 | |
|
13 | 13 | The following 25 authors contributed 337 commits. |
|
14 | 14 | |
|
15 | 15 | * Benjamin Ragan-Kelley |
|
16 | 16 | * Bing Xia |
|
17 | 17 | * Bradley M. Froehle |
|
18 | 18 | * Brian E. Granger |
|
19 | 19 | * DamiΓ‘n Avila |
|
20 | 20 | * dhirschfeld |
|
21 | 21 | * DraΕΎen LuΔanin |
|
22 | 22 | * gmbecker |
|
23 | 23 | * Jake Vanderplas |
|
24 | 24 | * Jason Grout |
|
25 | 25 | * Jonathan Frederic |
|
26 | 26 | * Kevin Burke |
|
27 | 27 | * Kyle Kelley |
|
28 | 28 | * Matt Henderson |
|
29 | 29 | * Matthew Brett |
|
30 | 30 | * Matthias Bussonnier |
|
31 | 31 | * Pankaj Pandey |
|
32 | 32 | * Paul Ivanov |
|
33 | 33 | * rossant |
|
34 | 34 | * Samuel Ainsworth |
|
35 | 35 | * Stephan Rave |
|
36 | 36 | * stonebig |
|
37 | 37 | * Thomas Kluyver |
|
38 | 38 | * Yaroslav Halchenko |
|
39 | 39 | * Zachary Sailer |
|
40 | 40 | |
|
41 | 41 | |
|
42 | 42 | We closed a total of 76 issues, 58 pull requests and 18 regular issues; |
|
43 | 43 | this is the full list (generated with the script :file:`tools/github_stats.py`): |
|
44 | 44 | |
|
45 | 45 | Pull Requests (58): |
|
46 | 46 | |
|
47 | 47 | * :ghpull:`4188`: Allow user_ns trait to be None |
|
48 | 48 | * :ghpull:`4189`: always fire LOCAL_IPS.extend(PUBLIC_IPS) |
|
49 | 49 | * :ghpull:`4174`: various issues in markdown and rst templates |
|
50 | 50 | * :ghpull:`4178`: add missing data_javascript |
|
51 | 51 | * :ghpull:`4181`: nbconvert: Fix, sphinx template not removing new lines from headers |
|
52 | 52 | * :ghpull:`4043`: don't 'restore_bytes' in from_JSON |
|
53 | 53 | * :ghpull:`4163`: Fix for incorrect default encoding on Windows. |
|
54 | 54 | * :ghpull:`4136`: catch javascript errors in any output |
|
55 | 55 | * :ghpull:`4171`: add nbconvert config file when creating profiles |
|
56 | 56 | * :ghpull:`4125`: Basic exercise of `ipython [subcommand] -h` and help-all |
|
57 | 57 | * :ghpull:`4085`: nbconvert: Fix sphinx preprocessor date format string for Windows |
|
58 | 58 | * :ghpull:`4159`: don't split `.cell` and `div.cell` CSS |
|
59 | 59 | * :ghpull:`4158`: generate choices for `--gui` configurable from real mapping |
|
60 | 60 | * :ghpull:`4065`: do not include specific css in embedable one |
|
61 | 61 | * :ghpull:`4092`: nbconvert: Fix for unicode html headers, Windows + Python 2.x |
|
62 | 62 | * :ghpull:`4074`: close Client sockets if connection fails |
|
63 | 63 | * :ghpull:`4064`: Store default codemirror mode in only 1 place |
|
64 | 64 | * :ghpull:`4104`: Add way to install MathJax to a particular profile |
|
65 | 65 | * :ghpull:`4144`: help_end transformer shouldn't pick up ? in multiline string |
|
66 | 66 | * :ghpull:`4143`: update example custom.js |
|
67 | 67 | * :ghpull:`4142`: DOC: unwrap openssl line in public_server doc |
|
68 | 68 | * :ghpull:`4141`: add files with a separate `add` call in backport_pr |
|
69 | 69 | * :ghpull:`4137`: Restore autorestore option for storemagic |
|
70 | 70 | * :ghpull:`4098`: pass profile-dir instead of profile name to Kernel |
|
71 | 71 | * :ghpull:`4120`: support `input` in Python 2 kernels |
|
72 | 72 | * :ghpull:`4088`: nbconvert: Fix coalescestreams line with incorrect nesting causing strange behavior |
|
73 | 73 | * :ghpull:`4060`: only strip continuation prompts if regular prompts seen first |
|
74 | 74 | * :ghpull:`4132`: Fixed name error bug in function safe_unicode in module py3compat. |
|
75 | 75 | * :ghpull:`4121`: move test_kernel from IPython.zmq to IPython.kernel |
|
76 | 76 | * :ghpull:`4118`: ZMQ heartbeat channel: catch EINTR exceptions and continue. |
|
77 | 77 | * :ghpull:`4054`: use unicode for HTML export |
|
78 | 78 | * :ghpull:`4106`: fix a couple of default block values |
|
79 | 79 | * :ghpull:`4115`: Update docs on declaring a magic function |
|
80 | 80 | * :ghpull:`4101`: restore accidentally removed EngineError |
|
81 | 81 | * :ghpull:`4096`: minor docs changes |
|
82 | 82 | * :ghpull:`4056`: respect `pylab_import_all` when `--pylab` specified at the command-line |
|
83 | 83 | * :ghpull:`4091`: Make Qt console banner configurable |
|
84 | 84 | * :ghpull:`4086`: fix missing errno import |
|
85 | 85 | * :ghpull:`4030`: exclude `.git` in MANIFEST.in |
|
86 | 86 | * :ghpull:`4047`: Use istype() when checking if canned object is a dict |
|
87 | 87 | * :ghpull:`4031`: don't close_fds on Windows |
|
88 | 88 | * :ghpull:`4029`: bson.Binary moved |
|
89 | 89 | * :ghpull:`4035`: Fixed custom jinja2 templates being ignored when setting template_path |
|
90 | 90 | * :ghpull:`4026`: small doc fix in nbconvert |
|
91 | 91 | * :ghpull:`4016`: Fix IPython.start_* functions |
|
92 | 92 | * :ghpull:`4021`: Fix parallel.client.View map() on numpy arrays |
|
93 | 93 | * :ghpull:`4022`: DOC: fix links to matplotlib, notebook docs |
|
94 | 94 | * :ghpull:`4018`: Fix warning when running IPython.kernel tests |
|
95 | 95 | * :ghpull:`4019`: Test skipping without unicode paths |
|
96 | 96 | * :ghpull:`4008`: Transform code before %prun/%%prun runs |
|
97 | 97 | * :ghpull:`4014`: Fix typo in ipapp |
|
98 | 98 | * :ghpull:`3987`: get files list in backport_pr |
|
99 | 99 | * :ghpull:`3974`: nbconvert: Fix app tests on Window7 w/ Python 3.3 |
|
100 | 100 | * :ghpull:`3978`: fix `--existing` with non-localhost IP |
|
101 | 101 | * :ghpull:`3939`: minor checkpoint cleanup |
|
102 | 102 | * :ghpull:`3981`: BF: fix nbconvert rst input prompt spacing |
|
103 | 103 | * :ghpull:`3960`: Don't make sphinx a dependency for importing nbconvert |
|
104 | 104 | * :ghpull:`3973`: logging.Formatter is not new-style in 2.6 |
|
105 | 105 | |
|
106 | 106 | Issues (18): |
|
107 | 107 | |
|
108 | 108 | * :ghissue:`4024`: nbconvert markdown issues |
|
109 | 109 | * :ghissue:`4095`: Catch js error in append html in stream/pyerr |
|
110 | 110 | * :ghissue:`4156`: Specifying --gui=tk at the command line |
|
111 | 111 | * :ghissue:`3818`: nbconvert can't handle Heading with Chinese characters on Japanese Windows OS. |
|
112 | 112 | * :ghissue:`4134`: multi-line parser fails on ''' in comment, qtconsole and notebook. |
|
113 | 113 | * :ghissue:`3998`: sample custom.js needs to be updated |
|
114 | 114 | * :ghissue:`4078`: StoreMagic.autorestore not working in 1.0.0 |
|
115 | 115 | * :ghissue:`3990`: Buitlin `input` doesn't work over zmq |
|
116 | 116 | * :ghissue:`4015`: nbconvert fails to convert all the content of a notebook |
|
117 | 117 | * :ghissue:`4059`: Issues with Ellipsis literal in Python 3 |
|
118 | 118 | * :ghissue:`4103`: Wrong default argument of DirectView.clear |
|
119 | 119 | * :ghissue:`4100`: parallel.client.client references undefined error.EngineError |
|
120 | 120 | * :ghissue:`4005`: IPython.start_kernel doesn't work. |
|
121 | 121 | * :ghissue:`4020`: IPython parallel map fails on numpy arrays |
|
122 | 122 | * :ghissue:`3945`: nbconvert: commandline tests fail Win7x64 Py3.3 |
|
123 | 123 | * :ghissue:`3977`: unable to complete remote connections for two-process |
|
124 | 124 | * :ghissue:`3980`: nbconvert rst output lacks needed blank lines |
|
125 | 125 | * :ghissue:`3968`: TypeError: super() argument 1 must be type, not classobj (Python 2.6.6) |
|
126 | 126 | |
|
127 | 127 | Issues closed in 1.0 |
|
128 | 128 | -------------------- |
|
129 | 129 | |
|
130 | 130 | GitHub stats for 2012/06/30 - 2013/08/08 (since 0.13) |
|
131 | 131 | |
|
132 | 132 | These lists are automatically generated, and may be incomplete or contain duplicates. |
|
133 | 133 | |
|
134 | 134 | The following 155 authors contributed 4258 commits. |
|
135 | 135 | |
|
136 | 136 | * Aaron Meurer |
|
137 | 137 | * Adam Davis |
|
138 | 138 | * Ahmet Bakan |
|
139 | 139 | * Alberto Valverde |
|
140 | 140 | * Allen Riddell |
|
141 | 141 | * Anders HovmΓΆller |
|
142 | 142 | * Andrea Bedini |
|
143 | 143 | * Andrew Spiers |
|
144 | 144 | * Andrew Vandever |
|
145 | 145 | * Anthony Scopatz |
|
146 | 146 | * Anton Akhmerov |
|
147 | 147 | * Anton I. Sipos |
|
148 | 148 | * Antony Lee |
|
149 | 149 | * Aron Ahmadia |
|
150 | 150 | * Benedikt Sauer |
|
151 | 151 | * Benjamin Jones |
|
152 | 152 | * Benjamin Ragan-Kelley |
|
153 | 153 | * Benjie Chen |
|
154 | 154 | * Boris de Laage |
|
155 | 155 | * Brad Reisfeld |
|
156 | 156 | * Bradley M. Froehle |
|
157 | 157 | * Brian E. Granger |
|
158 | 158 | * Cameron Bates |
|
159 | 159 | * Cavendish McKay |
|
160 | 160 | * chapmanb |
|
161 | 161 | * Chris Beaumont |
|
162 | 162 | * Chris Laumann |
|
163 | 163 | * Christoph Gohlke |
|
164 | 164 | * codebraker |
|
165 | 165 | * codespaced |
|
166 | 166 | * Corran Webster |
|
167 | 167 | * DamianHeard |
|
168 | 168 | * DamiΓ‘n Avila |
|
169 | 169 | * Dan Kilman |
|
170 | 170 | * Dan McDougall |
|
171 | 171 | * Danny Staple |
|
172 | 172 | * David Hirschfeld |
|
173 | 173 | * David P. Sanders |
|
174 | 174 | * David Warde-Farley |
|
175 | 175 | * David Wolever |
|
176 | 176 | * David Wyde |
|
177 | 177 | * debjan |
|
178 | 178 | * Diane Trout |
|
179 | 179 | * dkua |
|
180 | 180 | * Dominik Dabrowski |
|
181 | 181 | * Donald Curtis |
|
182 | 182 | * DraΕΎen LuΔanin |
|
183 | 183 | * drevicko |
|
184 | 184 | * Eric O. LEBIGOT |
|
185 | 185 | * Erik M. Bray |
|
186 | 186 | * Erik Tollerud |
|
187 | 187 | * Eugene Van den Bulke |
|
188 | 188 | * Evan Patterson |
|
189 | 189 | * Fernando Perez |
|
190 | 190 | * Francesco Montesano |
|
191 | 191 | * Frank Murphy |
|
192 | 192 | * Greg Caporaso |
|
193 | 193 | * Guy Haskin Fernald |
|
194 | 194 | * guziy |
|
195 | 195 | * Hans Meine |
|
196 | 196 | * Harry Moreno |
|
197 | 197 | * henryiii |
|
198 | 198 | * Ivan Djokic |
|
199 | 199 | * Jack Feser |
|
200 | 200 | * Jake Vanderplas |
|
201 | 201 | * jakobgager |
|
202 | 202 | * James Booth |
|
203 | 203 | * Jan Schulz |
|
204 | 204 | * Jason Grout |
|
205 | 205 | * Jeff Knisley |
|
206 | 206 | * Jens Hedegaard Nielsen |
|
207 | 207 | * jeremiahbuddha |
|
208 | 208 | * Jerry Fowler |
|
209 | 209 | * Jessica B. Hamrick |
|
210 | 210 | * Jez Ng |
|
211 | 211 | * John Zwinck |
|
212 | 212 | * Jonathan Frederic |
|
213 | 213 | * Jonathan Taylor |
|
214 | 214 | * Joon Ro |
|
215 | 215 | * Joseph Lansdowne |
|
216 | 216 | * Juergen Hasch |
|
217 | 217 | * Julian Taylor |
|
218 | 218 | * Jussi Sainio |
|
219 | 219 | * JΓΆrgen Stenarson |
|
220 | 220 | * kevin |
|
221 | 221 | * klonuo |
|
222 | 222 | * Konrad Hinsen |
|
223 | 223 | * Kyle Kelley |
|
224 | 224 | * Lars Solberg |
|
225 | 225 | * Lessandro Mariano |
|
226 | 226 | * Mark Sienkiewicz at STScI |
|
227 | 227 | * Martijn Vermaat |
|
228 | 228 | * Martin Spacek |
|
229 | 229 | * Matthias Bussonnier |
|
230 | 230 | * Maxim Grechkin |
|
231 | 231 | * Maximilian Albert |
|
232 | 232 | * MercuryRising |
|
233 | 233 | * Michael Droettboom |
|
234 | 234 | * Michael Shuffett |
|
235 | 235 | * MichaΕ GΓ³rny |
|
236 | 236 | * Mikhail Korobov |
|
237 | 237 | * mr.Shu |
|
238 | 238 | * Nathan Goldbaum |
|
239 | 239 | * ocefpaf |
|
240 | 240 | * Ohad Ravid |
|
241 | 241 | * Olivier Grisel |
|
242 | 242 | * Olivier Verdier |
|
243 | 243 | * Owen Healy |
|
244 | 244 | * Pankaj Pandey |
|
245 | 245 | * Paul Ivanov |
|
246 | 246 | * Pawel Jasinski |
|
247 | 247 | * Pietro Berkes |
|
248 | 248 | * Piti Ongmongkolkul |
|
249 | 249 | * Puneeth Chaganti |
|
250 | 250 | * Rich Wareham |
|
251 | 251 | * Richard Everson |
|
252 | 252 | * Rick Lupton |
|
253 | 253 | * Rob Young |
|
254 | 254 | * Robert Kern |
|
255 | 255 | * Robert Marchman |
|
256 | 256 | * Robert McGibbon |
|
257 | 257 | * Rui Pereira |
|
258 | 258 | * Rustam Safin |
|
259 | 259 | * Ryan May |
|
260 | 260 | * s8weber |
|
261 | 261 | * Samuel Ainsworth |
|
262 | 262 | * Sean Vig |
|
263 | 263 | * Siyu Zhang |
|
264 | 264 | * Skylar Saveland |
|
265 | 265 | * slojo404 |
|
266 | 266 | * smithj1 |
|
267 | 267 | * Stefan Karpinski |
|
268 | 268 | * Stefan van der Walt |
|
269 | 269 | * Steven Silvester |
|
270 | 270 | * Takafumi Arakaki |
|
271 | 271 | * Takeshi Kanmae |
|
272 | 272 | * tcmulcahy |
|
273 | 273 | * teegaar |
|
274 | 274 | * Thomas Kluyver |
|
275 | 275 | * Thomas Robitaille |
|
276 | 276 | * Thomas Spura |
|
277 | 277 | * Thomas WeiΓschuh |
|
278 | 278 | * Timothy O'Donnell |
|
279 | 279 | * Tom Dimiduk |
|
280 | 280 | * ugurthemaster |
|
281 | 281 | * urielshaolin |
|
282 | 282 | * v923z |
|
283 | 283 | * Valentin Haenel |
|
284 | 284 | * Victor Zverovich |
|
285 | 285 | * W. Trevor King |
|
286 | 286 | * y-p |
|
287 | 287 | * Yoav Ram |
|
288 | 288 | * Zbigniew JΔdrzejewski-Szmek |
|
289 | 289 | * ZoltΓ‘n VΓΆrΓΆs |
|
290 | 290 | |
|
291 | 291 | |
|
292 | 292 | We closed a total of 1484 issues, 793 pull requests and 691 regular issues; |
|
293 | 293 | this is the full list (generated with the script |
|
294 | 294 | :file:`tools/github_stats.py`): |
|
295 | 295 | |
|
296 | 296 | Pull Requests (793): |
|
297 | 297 | |
|
298 | 298 | * :ghpull:`3958`: doc update |
|
299 | 299 | * :ghpull:`3965`: Fix ansi color code for background yellow |
|
300 | 300 | * :ghpull:`3964`: Fix casing of message. |
|
301 | 301 | * :ghpull:`3942`: Pass on install docs |
|
302 | 302 | * :ghpull:`3962`: exclude IPython.lib.kernel in iptest |
|
303 | 303 | * :ghpull:`3961`: Longpath test fix |
|
304 | 304 | * :ghpull:`3905`: Remove references to 0.11 and 0.12 from config/overview.rst |
|
305 | 305 | * :ghpull:`3951`: nbconvert: fixed latex characters not escaped properly in nbconvert |
|
306 | 306 | * :ghpull:`3949`: log fatal error when PDF conversion fails |
|
307 | 307 | * :ghpull:`3947`: nbconvert: Make writer & post-processor aliases case insensitive. |
|
308 | 308 | * :ghpull:`3938`: Recompile css. |
|
309 | 309 | * :ghpull:`3948`: sphinx and PDF tweaks |
|
310 | 310 | * :ghpull:`3943`: nbconvert: Serve post-processor Windows fix |
|
311 | 311 | * :ghpull:`3934`: nbconvert: fix logic of verbose flag in PDF post processor |
|
312 | 312 | * :ghpull:`3929`: swallow enter event in rename dialog |
|
313 | 313 | * :ghpull:`3924`: nbconvert: Backport fixes |
|
314 | 314 | * :ghpull:`3925`: Replace --pylab flag with --matplotlib in usage |
|
315 | 315 | * :ghpull:`3910`: Added explicit error message for missing configuration arguments. |
|
316 | 316 | * :ghpull:`3913`: grffile to support spaces in notebook names |
|
317 | 317 | * :ghpull:`3918`: added check_for_tornado, closes #3916 |
|
318 | 318 | * :ghpull:`3917`: change docs/examples refs to be just examples |
|
319 | 319 | * :ghpull:`3908`: what's new tweaks |
|
320 | 320 | * :ghpull:`3896`: two column quickhelp dialog, closes #3895 |
|
321 | 321 | * :ghpull:`3911`: explicitly load python mode before IPython mode |
|
322 | 322 | * :ghpull:`3901`: don't force . relative path, fix #3897 |
|
323 | 323 | * :ghpull:`3891`: fix #3889 |
|
324 | 324 | * :ghpull:`3892`: Fix documentation of Kernel.stop_channels |
|
325 | 325 | * :ghpull:`3888`: posixify paths for Windows latex |
|
326 | 326 | * :ghpull:`3882`: quick fix for #3881 |
|
327 | 327 | * :ghpull:`3877`: don't use `shell=True` in PDF export |
|
328 | 328 | * :ghpull:`3878`: minor template loading cleanup |
|
329 | 329 | * :ghpull:`3855`: nbconvert: Filter tests |
|
330 | 330 | * :ghpull:`3879`: finish 3870 |
|
331 | 331 | * :ghpull:`3870`: Fix for converting notebooks that contain unicode characters. |
|
332 | 332 | * :ghpull:`3876`: Update parallel_winhpc.rst |
|
333 | 333 | * :ghpull:`3872`: removing vim-ipython, since it has it's own repo |
|
334 | 334 | * :ghpull:`3871`: updating docs |
|
335 | 335 | * :ghpull:`3873`: remove old examples |
|
336 | 336 | * :ghpull:`3868`: update CodeMirror component to 3.15 |
|
337 | 337 | * :ghpull:`3865`: Escape filename for pdflatex in nbconvert |
|
338 | 338 | * :ghpull:`3861`: remove old external.js |
|
339 | 339 | * :ghpull:`3864`: add keyboard shortcut to docs |
|
340 | 340 | * :ghpull:`3834`: This PR fixes a few issues with nbconvert tests |
|
341 | 341 | * :ghpull:`3840`: prevent profile_dir from being undefined |
|
342 | 342 | * :ghpull:`3859`: Add "An Afternoon Hack" to docs |
|
343 | 343 | * :ghpull:`3854`: Catch errors filling readline history on startup |
|
344 | 344 | * :ghpull:`3857`: Delete extra auto |
|
345 | 345 | * :ghpull:`3845`: nbconvert: Serve from original build directory |
|
346 | 346 | * :ghpull:`3846`: Add basic logging to nbconvert |
|
347 | 347 | * :ghpull:`3850`: add missing store_history key to Notebook execute_requests |
|
348 | 348 | * :ghpull:`3844`: update payload source |
|
349 | 349 | * :ghpull:`3830`: mention metadata / display_data similarity in pyout spec |
|
350 | 350 | * :ghpull:`3848`: fix incorrect `empty-docstring` |
|
351 | 351 | * :ghpull:`3836`: Parse markdown correctly when mathjax is disabled |
|
352 | 352 | * :ghpull:`3849`: skip a failing test on windows |
|
353 | 353 | * :ghpull:`3828`: signature_scheme lives in Session |
|
354 | 354 | * :ghpull:`3831`: update nbconvert doc with new CLI |
|
355 | 355 | * :ghpull:`3822`: add output flag to nbconvert |
|
356 | 356 | * :ghpull:`3780`: Added serving the output directory if html-based format are selected. |
|
357 | 357 | * :ghpull:`3764`: Cleanup nbconvert templates |
|
358 | 358 | * :ghpull:`3829`: remove now-duplicate 'this is dev' note |
|
359 | 359 | * :ghpull:`3814`: add `ConsoleWidget.execute_on_complete_input` flag |
|
360 | 360 | * :ghpull:`3826`: try rtfd |
|
361 | 361 | * :ghpull:`3821`: add sphinx prolog |
|
362 | 362 | * :ghpull:`3817`: relax timeouts in terminal console and tests |
|
363 | 363 | * :ghpull:`3825`: fix more tests that fail when pandoc is missing |
|
364 | 364 | * :ghpull:`3824`: don't set target on internal markdown links |
|
365 | 365 | * :ghpull:`3816`: s/pylab/matplotlib in docs |
|
366 | 366 | * :ghpull:`3812`: Describe differences between start_ipython and embed |
|
367 | 367 | * :ghpull:`3805`: Print View has been removed |
|
368 | 368 | * :ghpull:`3820`: Make it clear that 1.0 is not released yet |
|
369 | 369 | * :ghpull:`3784`: nbconvert: Export flavors & PDF writer (ipy dev meeting) |
|
370 | 370 | * :ghpull:`3800`: semantic-versionify version number for non-releases |
|
371 | 371 | * :ghpull:`3802`: Documentation .txt to .rst |
|
372 | 372 | * :ghpull:`3765`: cleanup terminal console iopub handling |
|
373 | 373 | * :ghpull:`3720`: Fix for #3719 |
|
374 | 374 | * :ghpull:`3787`: re-raise KeyboardInterrupt in raw_input |
|
375 | 375 | * :ghpull:`3770`: Organizing reveal's templates. |
|
376 | 376 | * :ghpull:`3751`: Use link(2) when possible in nbconvert |
|
377 | 377 | * :ghpull:`3792`: skip tests that require pandoc |
|
378 | 378 | * :ghpull:`3782`: add Importing Notebooks example |
|
379 | 379 | * :ghpull:`3752`: nbconvert: Add cwd to sys.path |
|
380 | 380 | * :ghpull:`3789`: fix raw_input in qtconsole |
|
381 | 381 | * :ghpull:`3756`: document the wire protocol |
|
382 | 382 | * :ghpull:`3749`: convert IPython syntax to Python syntax in nbconvert python template |
|
383 | 383 | * :ghpull:`3793`: Closes #3788 |
|
384 | 384 | * :ghpull:`3794`: Change logo link to ipython.org |
|
385 | 385 | * :ghpull:`3746`: Raise a named exception when pandoc is missing |
|
386 | 386 | * :ghpull:`3781`: comply with the message spec in the notebook |
|
387 | 387 | * :ghpull:`3779`: remove bad `if logged_in` preventing new-notebook without login |
|
388 | 388 | * :ghpull:`3743`: remove notebook read-only view |
|
389 | 389 | * :ghpull:`3732`: add delay to autosave in beforeunload |
|
390 | 390 | * :ghpull:`3761`: Added rm_math_space to markdown cells in the basichtml.tpl to be rendered ok by mathjax after the nbconvertion. |
|
391 | 391 | * :ghpull:`3758`: nbconvert: Filter names cleanup |
|
392 | 392 | * :ghpull:`3769`: Add configurability to tabcompletion timeout |
|
393 | 393 | * :ghpull:`3771`: Update px pylab test to match new output of pylab |
|
394 | 394 | * :ghpull:`3741`: better message when notebook format is not supported |
|
395 | 395 | * :ghpull:`3753`: document Ctrl-C not working in ipython kernel |
|
396 | 396 | * :ghpull:`3766`: handle empty metadata in pyout messages more gracefully. |
|
397 | 397 | * :ghpull:`3736`: my attempt to fix #3735 |
|
398 | 398 | * :ghpull:`3759`: nbconvert: Provide a more useful error for invalid use case. |
|
399 | 399 | * :ghpull:`3760`: nbconvert: Allow notebook filenames without their extensions |
|
400 | 400 | * :ghpull:`3750`: nbconvert: Add cwd to default templates search path. |
|
401 | 401 | * :ghpull:`3748`: Update nbconvert docs |
|
402 | 402 | * :ghpull:`3734`: Nbconvert: Export extracted files into `nbname_files` subdirectory |
|
403 | 403 | * :ghpull:`3733`: Nicer message when pandoc is missing, closes #3730 |
|
404 | 404 | * :ghpull:`3722`: fix two failing test in IPython.lib |
|
405 | 405 | * :ghpull:`3704`: Start what's new for 1.0 |
|
406 | 406 | * :ghpull:`3705`: Complete rewrite of IPython Notebook documentation: docs/source/interactive/htmlnotebook.txt |
|
407 | 407 | * :ghpull:`3709`: Docs cleanup |
|
408 | 408 | * :ghpull:`3716`: raw_input fixes for kernel restarts |
|
409 | 409 | * :ghpull:`3683`: use `%matplotlib` in example notebooks |
|
410 | 410 | * :ghpull:`3686`: remove quarantine |
|
411 | 411 | * :ghpull:`3699`: svg2pdf unicode fix |
|
412 | 412 | * :ghpull:`3695`: fix SVG2PDF |
|
413 | 413 | * :ghpull:`3685`: fix Pager.detach |
|
414 | 414 | * :ghpull:`3675`: document new dependencies |
|
415 | 415 | * :ghpull:`3690`: Fixing some css minors in full_html and reveal. |
|
416 | 416 | * :ghpull:`3671`: nbconvert tests |
|
417 | 417 | * :ghpull:`3692`: Fix rename notebook - show error with invalid name |
|
418 | 418 | * :ghpull:`3409`: Prevent qtconsole frontend freeze on lots of output. |
|
419 | 419 | * :ghpull:`3660`: refocus active cell on dialog close |
|
420 | 420 | * :ghpull:`3598`: Statelessify mathjaxutils |
|
421 | 421 | * :ghpull:`3673`: enable comment/uncomment selection |
|
422 | 422 | * :ghpull:`3677`: remove special-case in get_home_dir for frozen dists |
|
423 | 423 | * :ghpull:`3674`: add CONTRIBUTING.md |
|
424 | 424 | * :ghpull:`3670`: use Popen command list for ipexec |
|
425 | 425 | * :ghpull:`3568`: pylab import adjustments |
|
426 | 426 | * :ghpull:`3559`: add create.Cell and delete.Cell js events |
|
427 | 427 | * :ghpull:`3606`: push cell magic to the head of the transformer line |
|
428 | 428 | * :ghpull:`3607`: NbConvert: Writers, No YAML, and stuff... |
|
429 | 429 | * :ghpull:`3665`: Pywin32 skips |
|
430 | 430 | * :ghpull:`3669`: set default client_class for QtKernelManager |
|
431 | 431 | * :ghpull:`3662`: add strip_encoding_cookie transformer |
|
432 | 432 | * :ghpull:`3641`: increase patience for slow kernel startup in tests |
|
433 | 433 | * :ghpull:`3651`: remove a bunch of unused `default_config_file` assignments |
|
434 | 434 | * :ghpull:`3630`: CSS adjustments |
|
435 | 435 | * :ghpull:`3645`: Don't require HistoryManager to have a shell |
|
436 | 436 | * :ghpull:`3643`: don't assume tested ipython is on the PATH |
|
437 | 437 | * :ghpull:`3654`: fix single-result AsyncResults |
|
438 | 438 | * :ghpull:`3601`: Markdown in heading cells (take 2) |
|
439 | 439 | * :ghpull:`3652`: Remove old `docs/examples` |
|
440 | 440 | * :ghpull:`3621`: catch any exception appending output |
|
441 | 441 | * :ghpull:`3585`: don't blacklist builtin names |
|
442 | 442 | * :ghpull:`3647`: Fix `frontend` deprecation warnings in several examples |
|
443 | 443 | * :ghpull:`3649`: fix AsyncResult.get_dict for single result |
|
444 | 444 | * :ghpull:`3648`: Fix store magic test |
|
445 | 445 | * :ghpull:`3650`: Fix, config_file_name was ignored |
|
446 | 446 | * :ghpull:`3640`: Gcf.get_active() can return None |
|
447 | 447 | * :ghpull:`3571`: Added shorcuts to split cell, merge cell above and merge cell below. |
|
448 | 448 | * :ghpull:`3635`: Added missing slash to print-pdf call. |
|
449 | 449 | * :ghpull:`3487`: Drop patch for compatibility with pyreadline 1.5 |
|
450 | 450 | * :ghpull:`3338`: Allow filename with extension in find_cmd in Windows. |
|
451 | 451 | * :ghpull:`3628`: Fix test for Python 3 on Windows. |
|
452 | 452 | * :ghpull:`3642`: Fix typo in docs |
|
453 | 453 | * :ghpull:`3627`: use DEFAULT_STATIC_FILES_PATH in a test instead of package dir |
|
454 | 454 | * :ghpull:`3624`: fix some unicode in zmqhandlers |
|
455 | 455 | * :ghpull:`3460`: Set calling program to UNKNOWN, when argv not in sys |
|
456 | 456 | * :ghpull:`3632`: Set calling program to UNKNOWN, when argv not in sys (take #2) |
|
457 | 457 | * :ghpull:`3629`: Use new entry point for python -m IPython |
|
458 | 458 | * :ghpull:`3626`: passing cell to showInPager, closes #3625 |
|
459 | 459 | * :ghpull:`3618`: expand terminal color support |
|
460 | 460 | * :ghpull:`3623`: raise UsageError for unsupported GUI backends |
|
461 | 461 | * :ghpull:`3071`: Add magic function %drun to run code in debugger |
|
462 | 462 | * :ghpull:`3608`: a nicer error message when using %pylab magic |
|
463 | 463 | * :ghpull:`3592`: add extra_config_file |
|
464 | 464 | * :ghpull:`3612`: updated .mailmap |
|
465 | 465 | * :ghpull:`3616`: Add examples for interactive use of MPI. |
|
466 | 466 | * :ghpull:`3615`: fix regular expression for ANSI escapes |
|
467 | 467 | * :ghpull:`3586`: Corrected a typo in the format string for strftime the sphinx.py transformer of nbconvert |
|
468 | 468 | * :ghpull:`3611`: check for markdown no longer needed, closes #3610 |
|
469 | 469 | * :ghpull:`3555`: Simplify caching of modules with %run |
|
470 | 470 | * :ghpull:`3583`: notebook small things |
|
471 | 471 | * :ghpull:`3594`: Fix duplicate completion in notebook |
|
472 | 472 | * :ghpull:`3600`: parallel: Improved logging for errors during BatchSystemLauncher.stop |
|
473 | 473 | * :ghpull:`3595`: Revert "allow markdown in heading cells" |
|
474 | 474 | * :ghpull:`3538`: add IPython.start_ipython |
|
475 | 475 | * :ghpull:`3562`: Allow custom nbconvert template loaders |
|
476 | 476 | * :ghpull:`3582`: pandoc adjustments |
|
477 | 477 | * :ghpull:`3560`: Remove max_msg_size |
|
478 | 478 | * :ghpull:`3591`: Refer to Setuptools instead of Distribute |
|
479 | 479 | * :ghpull:`3590`: IPython.sphinxext needs an __init__.py |
|
480 | 480 | * :ghpull:`3581`: Added the possibility to read a custom.css file for tweaking the final html in full_html and reveal templates. |
|
481 | 481 | * :ghpull:`3576`: Added support for markdown in heading cells when they are nbconverted. |
|
482 | 482 | * :ghpull:`3575`: tweak `run -d` message to 'continue execution' |
|
483 | 483 | * :ghpull:`3569`: add PYTHONSTARTUP to startup files |
|
484 | 484 | * :ghpull:`3567`: Trigger a single event on js app initilized |
|
485 | 485 | * :ghpull:`3565`: style.min.css shoudl always exist... |
|
486 | 486 | * :ghpull:`3531`: allow markdown in heading cells |
|
487 | 487 | * :ghpull:`3577`: Simplify codemirror ipython-mode |
|
488 | 488 | * :ghpull:`3495`: Simplified regexp, and suggestions for clearer regexps. |
|
489 | 489 | * :ghpull:`3578`: Use adjustbox to specify figure size in nbconvert -> latex |
|
490 | 490 | * :ghpull:`3572`: Skip import irunner test on Windows. |
|
491 | 491 | * :ghpull:`3574`: correct static path for CM modes autoload |
|
492 | 492 | * :ghpull:`3558`: Add IPython.sphinxext |
|
493 | 493 | * :ghpull:`3561`: mention double-control-C to stop notebook server |
|
494 | 494 | * :ghpull:`3566`: fix event names |
|
495 | 495 | * :ghpull:`3564`: Remove trivial nbconvert example |
|
496 | 496 | * :ghpull:`3540`: allow cython cache dir to be deleted |
|
497 | 497 | * :ghpull:`3527`: cleanup stale, unused exceptions in parallel.error |
|
498 | 498 | * :ghpull:`3529`: ensure raw_input returns str in zmq shell |
|
499 | 499 | * :ghpull:`3541`: respect image size metadata in qtconsole |
|
500 | 500 | * :ghpull:`3550`: Fixing issue preventing the correct read of images by full_html and reveal exporters. |
|
501 | 501 | * :ghpull:`3557`: open markdown links in new tabs |
|
502 | 502 | * :ghpull:`3556`: remove mention of nonexistent `_margv` in macro |
|
503 | 503 | * :ghpull:`3552`: set overflow-x: hidden on Firefox only |
|
504 | 504 | * :ghpull:`3554`: Fix missing import os in latex exporter. |
|
505 | 505 | * :ghpull:`3546`: Don't hardcode **latex** posix paths in nbconvert |
|
506 | 506 | * :ghpull:`3551`: fix path prefix in nbconvert |
|
507 | 507 | * :ghpull:`3533`: Use a CDN to get reveal.js library. |
|
508 | 508 | * :ghpull:`3498`: When a notebook is written to file, name the metadata name u''. |
|
509 | 509 | * :ghpull:`3548`: Change to standard save icon in Notebook toolbar |
|
510 | 510 | * :ghpull:`3539`: Don't hardcode posix paths in nbconvert |
|
511 | 511 | * :ghpull:`3508`: notebook supports raw_input and %debug now |
|
512 | 512 | * :ghpull:`3526`: ensure 'default' is first in cluster profile list |
|
513 | 513 | * :ghpull:`3525`: basic timezone info |
|
514 | 514 | * :ghpull:`3532`: include nbconvert templates in installation |
|
515 | 515 | * :ghpull:`3515`: update CodeMirror component to 3.14 |
|
516 | 516 | * :ghpull:`3513`: add 'No Checkpoints' to Revert menu |
|
517 | 517 | * :ghpull:`3536`: format positions are required in Python 2.6.x |
|
518 | 518 | * :ghpull:`3521`: Nbconvert fix, silent fail if template doesn't exist |
|
519 | 519 | * :ghpull:`3530`: update %store magic docstring |
|
520 | 520 | * :ghpull:`3528`: fix local mathjax with custom base_project_url |
|
521 | 521 | * :ghpull:`3518`: Clear up unused imports |
|
522 | 522 | * :ghpull:`3506`: %store -r restores saved aliases and directory history, as well as variables |
|
523 | 523 | * :ghpull:`3516`: make css highlight style configurable |
|
524 | 524 | * :ghpull:`3523`: Exclude frontend shim from docs build |
|
525 | 525 | * :ghpull:`3514`: use bootstrap `disabled` instead of `ui-state-disabled` |
|
526 | 526 | * :ghpull:`3520`: Added relative import of RevealExporter to __init__.py inside exporters module |
|
527 | 527 | * :ghpull:`3507`: fix HTML capitalization in nbconvert exporter classes |
|
528 | 528 | * :ghpull:`3512`: fix nbconvert filter validation |
|
529 | 529 | * :ghpull:`3511`: Get Tracer working after ipapi.get replaced with get_ipython |
|
530 | 530 | * :ghpull:`3510`: use `window.onbeforeunload=` for nav-away warning |
|
531 | 531 | * :ghpull:`3504`: don't use parent=self in handlers |
|
532 | 532 | * :ghpull:`3500`: Merge nbconvert into IPython |
|
533 | 533 | * :ghpull:`3478`: restore "unsaved changes" warning on unload |
|
534 | 534 | * :ghpull:`3493`: add a dialog when the kernel is auto-restarted |
|
535 | 535 | * :ghpull:`3488`: Add test suite for autoreload extension |
|
536 | 536 | * :ghpull:`3484`: Catch some pathological cases inside oinspect |
|
537 | 537 | * :ghpull:`3481`: Display R errors without Python traceback |
|
538 | 538 | * :ghpull:`3468`: fix `%magic` output |
|
539 | 539 | * :ghpull:`3430`: add parent to Configurable |
|
540 | 540 | * :ghpull:`3491`: Remove unexpected keyword parameter to remove_kernel |
|
541 | 541 | * :ghpull:`3485`: SymPy has changed its recommended way to initialize printing |
|
542 | 542 | * :ghpull:`3486`: Add test for non-ascii characters in docstrings |
|
543 | 543 | * :ghpull:`3483`: Inputtransformer: Allow classic prompts without space |
|
544 | 544 | * :ghpull:`3482`: Use an absolute path to iptest, because the tests are not always run from $IPYTHONDIR. |
|
545 | 545 | * :ghpull:`3381`: enable 2x (retina) display |
|
546 | 546 | * :ghpull:`3450`: Flatten IPython.frontend |
|
547 | 547 | * :ghpull:`3477`: pass config to subapps |
|
548 | 548 | * :ghpull:`3466`: Kernel fails to start when username has non-ascii characters |
|
549 | 549 | * :ghpull:`3465`: Add HTCondor bindings to IPython.parallel |
|
550 | 550 | * :ghpull:`3463`: fix typo, closes #3462 |
|
551 | 551 | * :ghpull:`3456`: Notice for users who disable javascript |
|
552 | 552 | * :ghpull:`3453`: fix cell execution in firefox, closes #3447 |
|
553 | 553 | * :ghpull:`3393`: [WIP] bootstrapify |
|
554 | 554 | * :ghpull:`3440`: Fix installing mathjax from downloaded file via command line |
|
555 | 555 | * :ghpull:`3431`: Provide means for starting the Qt console maximized and with the menu bar hidden |
|
556 | 556 | * :ghpull:`3425`: base IPClusterApp inherits from BaseIPythonApp |
|
557 | 557 | * :ghpull:`3433`: Update IPython\external\path\__init__.py |
|
558 | 558 | * :ghpull:`3298`: Some fixes in IPython Sphinx directive |
|
559 | 559 | * :ghpull:`3428`: process escapes in mathjax |
|
560 | 560 | * :ghpull:`3420`: thansk -> thanks |
|
561 | 561 | * :ghpull:`3416`: Fix doc: "principle" not "principal" |
|
562 | 562 | * :ghpull:`3413`: more unique filename for test |
|
563 | 563 | * :ghpull:`3364`: Inject requirejs in notebook and start using it. |
|
564 | 564 | * :ghpull:`3390`: Fix %paste with blank lines |
|
565 | 565 | * :ghpull:`3403`: fix creating config objects from dicts |
|
566 | 566 | * :ghpull:`3401`: rollback #3358 |
|
567 | 567 | * :ghpull:`3373`: make cookie_secret configurable |
|
568 | 568 | * :ghpull:`3307`: switch default ws_url logic to js side |
|
569 | 569 | * :ghpull:`3392`: Restore anchor link on h2-h6 |
|
570 | 570 | * :ghpull:`3369`: Use different treshold for (auto)scroll in output |
|
571 | 571 | * :ghpull:`3370`: normalize unicode notebook filenames |
|
572 | 572 | * :ghpull:`3372`: base default cookie name on request host+port |
|
573 | 573 | * :ghpull:`3378`: disable CodeMirror drag/drop on Safari |
|
574 | 574 | * :ghpull:`3358`: workaround spurious CodeMirror scrollbars |
|
575 | 575 | * :ghpull:`3371`: make setting the notebook dirty flag an event |
|
576 | 576 | * :ghpull:`3366`: remove long-dead zmq frontend.py and completer.py |
|
577 | 577 | * :ghpull:`3382`: cull Session digest history |
|
578 | 578 | * :ghpull:`3330`: Fix get_ipython_dir when $HOME is / |
|
579 | 579 | * :ghpull:`3319`: IPEP 13: user-expressions and user-variables |
|
580 | 580 | * :ghpull:`3384`: comments in tools/gitwash_dumper.py changed (''' to """) |
|
581 | 581 | * :ghpull:`3387`: Make submodule checks work under Python 3. |
|
582 | 582 | * :ghpull:`3357`: move anchor-link off of heading text |
|
583 | 583 | * :ghpull:`3351`: start basic tests of ipcluster Launchers |
|
584 | 584 | * :ghpull:`3377`: allow class.__module__ to be None |
|
585 | 585 | * :ghpull:`3340`: skip submodule check in package managers |
|
586 | 586 | * :ghpull:`3328`: decode subprocess output in launchers |
|
587 | 587 | * :ghpull:`3368`: Reenable bracket matching |
|
588 | 588 | * :ghpull:`3356`: Mpr fixes |
|
589 | 589 | * :ghpull:`3336`: Use new input transformation API in %time magic |
|
590 | 590 | * :ghpull:`3325`: Organize the JS and less files by component. |
|
591 | 591 | * :ghpull:`3342`: fix test_find_cmd_python |
|
592 | 592 | * :ghpull:`3354`: catch socket.error in utils.localinterfaces |
|
593 | 593 | * :ghpull:`3341`: fix default cluster count |
|
594 | 594 | * :ghpull:`3286`: don't use `get_ipython` from builtins in library code |
|
595 | 595 | * :ghpull:`3333`: notebookapp: add missing whitespace to warnings |
|
596 | 596 | * :ghpull:`3323`: Strip prompts even if the prompt isn't present on the first line. |
|
597 | 597 | * :ghpull:`3321`: Reorganize the python/server side of the notebook |
|
598 | 598 | * :ghpull:`3320`: define `__file__` in config files |
|
599 | 599 | * :ghpull:`3317`: rename `%%file` to `%%writefile` |
|
600 | 600 | * :ghpull:`3304`: set unlimited HWM for all relay devices |
|
601 | 601 | * :ghpull:`3315`: Update Sympy_printing extension load |
|
602 | 602 | * :ghpull:`3310`: further clarify Image docstring |
|
603 | 603 | * :ghpull:`3285`: load extensions in builtin trap |
|
604 | 604 | * :ghpull:`3308`: Speed up AsyncResult._wait_for_outputs(0) |
|
605 | 605 | * :ghpull:`3294`: fix callbacks as optional in js kernel.execute |
|
606 | 606 | * :ghpull:`3276`: Fix: "python ABS/PATH/TO/ipython.py" fails |
|
607 | 607 | * :ghpull:`3301`: allow python3 tests without python installed |
|
608 | 608 | * :ghpull:`3282`: allow view.map to work with a few more things |
|
609 | 609 | * :ghpull:`3284`: remove `ipython.py` entry point |
|
610 | 610 | * :ghpull:`3281`: fix ignored IOPub messages with no parent |
|
611 | 611 | * :ghpull:`3275`: improve submodule messages / git hooks |
|
612 | 612 | * :ghpull:`3239`: Allow "x" icon and esc key to close pager in notebook |
|
613 | 613 | * :ghpull:`3290`: Improved heartbeat controller to engine monitoring for long running tasks |
|
614 | 614 | * :ghpull:`3142`: Better error message when CWD doesn't exist on startup |
|
615 | 615 | * :ghpull:`3066`: Add support for relative import to %run -m (fixes #2727) |
|
616 | 616 | * :ghpull:`3269`: protect highlight.js against unknown languages |
|
617 | 617 | * :ghpull:`3267`: add missing return |
|
618 | 618 | * :ghpull:`3101`: use marked / highlight.js instead of pagedown and prettify |
|
619 | 619 | * :ghpull:`3264`: use https url for submodule |
|
620 | 620 | * :ghpull:`3263`: fix set_last_checkpoint when no checkpoint |
|
621 | 621 | * :ghpull:`3258`: Fix submodule location in setup.py |
|
622 | 622 | * :ghpull:`3254`: fix a few URLs from previous PR |
|
623 | 623 | * :ghpull:`3240`: remove js components from the repo |
|
624 | 624 | * :ghpull:`3158`: IPEP 15: autosave the notebook |
|
625 | 625 | * :ghpull:`3252`: move images out of _static folder into _images |
|
626 | 626 | * :ghpull:`3251`: Fix for cell magics in Qt console |
|
627 | 627 | * :ghpull:`3250`: Added a simple __html__() method to the HTML class |
|
628 | 628 | * :ghpull:`3249`: remove copy of sphinx inheritance_diagram.py |
|
629 | 629 | * :ghpull:`3235`: Remove the unused print notebook view |
|
630 | 630 | * :ghpull:`3238`: Improve the design of the tab completion UI |
|
631 | 631 | * :ghpull:`3242`: Make changes of Application.log_format effective |
|
632 | 632 | * :ghpull:`3219`: Workaround so only one CTRL-C is required for a new prompt in --gui=qt |
|
633 | 633 | * :ghpull:`3190`: allow formatters to specify metadata |
|
634 | 634 | * :ghpull:`3231`: improve discovery of public IPs |
|
635 | 635 | * :ghpull:`3233`: check prefixes for swallowing kernel args |
|
636 | 636 | * :ghpull:`3234`: Removing old autogrow JS code. |
|
637 | 637 | * :ghpull:`3232`: Update to CodeMirror 3 and start to ship our components |
|
638 | 638 | * :ghpull:`3229`: The HTML output type accidentally got removed from the OutputArea. |
|
639 | 639 | * :ghpull:`3228`: Typo in IPython.Parallel documentation |
|
640 | 640 | * :ghpull:`3226`: Text in rename dialog was way too big - making it <p>. |
|
641 | 641 | * :ghpull:`3225`: Removing old restuctured text handler and web service. |
|
642 | 642 | * :ghpull:`3222`: make BlockingKernelClient the default Client |
|
643 | 643 | * :ghpull:`3223`: add missing mathjax_url to new settings dict |
|
644 | 644 | * :ghpull:`3089`: add stdin to the notebook |
|
645 | 645 | * :ghpull:`3221`: Remove references to HTMLCell (dead code) |
|
646 | * :ghpull:`3205`: add ignored *args to HasTraits constructor | |
|
646 | * :ghpull:`3205`: add ignored ``*args`` to HasTraits constructor | |
|
647 | 647 | * :ghpull:`3088`: cleanup IPython handler settings |
|
648 | 648 | * :ghpull:`3201`: use much faster regexp for ansi coloring |
|
649 | 649 | * :ghpull:`3220`: avoid race condition in profile creation |
|
650 | 650 | * :ghpull:`3011`: IPEP 12: add KernelClient |
|
651 | 651 | * :ghpull:`3217`: informative error when trying to load directories |
|
652 | 652 | * :ghpull:`3174`: Simple class |
|
653 | 653 | * :ghpull:`2979`: CM configurable Take 2 |
|
654 | 654 | * :ghpull:`3215`: Updates storemagic extension to allow for specifying variable name to load |
|
655 | 655 | * :ghpull:`3181`: backport If-Modified-Since fix from tornado |
|
656 | 656 | * :ghpull:`3200`: IFrame (VimeoVideo, ScribdDocument, ...) |
|
657 | 657 | * :ghpull:`3186`: Fix small inconsistency in nbconvert: etype -> ename |
|
658 | 658 | * :ghpull:`3212`: Fix issue #2563, "core.profiledir.check_startup_dir() doesn't work inside py2exe'd installation" |
|
659 | 659 | * :ghpull:`3211`: Fix inheritance_diagram Sphinx extension for Sphinx 1.2 |
|
660 | 660 | * :ghpull:`3208`: Update link to extensions index |
|
661 | 661 | * :ghpull:`3203`: Separate InputSplitter for transforming whole cells |
|
662 | 662 | * :ghpull:`3189`: Improve completer |
|
663 | 663 | * :ghpull:`3194`: finish up PR #3116 |
|
664 | 664 | * :ghpull:`3188`: Add new keycodes |
|
665 | 665 | * :ghpull:`2695`: Key the root modules cache by sys.path entries. |
|
666 | 666 | * :ghpull:`3182`: clarify %%file docstring |
|
667 | 667 | * :ghpull:`3163`: BUG: Fix the set and frozenset pretty printer to handle the empty case correctly |
|
668 | 668 | * :ghpull:`3180`: better UsageError for cell magic with no body |
|
669 | 669 | * :ghpull:`3184`: Cython cache |
|
670 | 670 | * :ghpull:`3175`: Added missing s |
|
671 | 671 | * :ghpull:`3173`: Little bits of documentation cleanup |
|
672 | 672 | * :ghpull:`2635`: Improve Windows start menu shortcuts (#2) |
|
673 | 673 | * :ghpull:`3172`: Add missing import in IPython parallel magics example |
|
674 | 674 | * :ghpull:`3170`: default application logger shouldn't propagate |
|
675 | 675 | * :ghpull:`3159`: Autocompletion for zsh |
|
676 | 676 | * :ghpull:`3105`: move DEFAULT_STATIC_FILES_PATH to IPython.html |
|
677 | 677 | * :ghpull:`3144`: minor bower tweaks |
|
678 | 678 | * :ghpull:`3141`: Default color output for ls on OSX |
|
679 | 679 | * :ghpull:`3137`: fix dot syntax error in inheritance diagram |
|
680 | 680 | * :ghpull:`3072`: raise UnsupportedOperation on iostream.fileno() |
|
681 | 681 | * :ghpull:`3147`: Notebook support for a reverse proxy which handles SSL |
|
682 | 682 | * :ghpull:`3152`: make qtconsole size at startup configurable |
|
683 | 683 | * :ghpull:`3162`: adding stream kwarg to current.new_output |
|
684 | 684 | * :ghpull:`2981`: IPEP 10: kernel side filtering of display formats |
|
685 | 685 | * :ghpull:`3058`: add redirect handler for notebooks by name |
|
686 | 686 | * :ghpull:`3041`: support non-modules in @require |
|
687 | 687 | * :ghpull:`2447`: Stateful line transformers |
|
688 | 688 | * :ghpull:`3108`: fix some O(N) and O(N^2) operations in parallel.map |
|
689 | 689 | * :ghpull:`2791`: forward stdout from forked processes |
|
690 | 690 | * :ghpull:`3157`: use Python 3-style for pretty-printed sets |
|
691 | 691 | * :ghpull:`3148`: closes #3045, #3123 for tornado < version 3.0 |
|
692 | 692 | * :ghpull:`3143`: minor heading-link tweaks |
|
693 | 693 | * :ghpull:`3136`: Strip useless ANSI escape codes in notebook |
|
694 | 694 | * :ghpull:`3126`: Prevent errors when pressing arrow keys in an empty notebook |
|
695 | 695 | * :ghpull:`3135`: quick dev installation instructions |
|
696 | 696 | * :ghpull:`2889`: Push pandas dataframes to R magic |
|
697 | 697 | * :ghpull:`3068`: Don't monkeypatch doctest during IPython startup. |
|
698 | 698 | * :ghpull:`3133`: fix argparse version check |
|
699 | 699 | * :ghpull:`3102`: set `spellcheck=false` in CodeCell inputarea |
|
700 | 700 | * :ghpull:`3064`: add anchors to heading cells |
|
701 | 701 | * :ghpull:`3097`: PyQt 4.10: use self._document = self.document() |
|
702 | 702 | * :ghpull:`3117`: propagate automagic change to shell |
|
703 | 703 | * :ghpull:`3118`: don't give up on weird os names |
|
704 | 704 | * :ghpull:`3115`: Fix example |
|
705 | 705 | * :ghpull:`2640`: fix quarantine/ipy_editors.py |
|
706 | 706 | * :ghpull:`3070`: Add info make target that was missing in old Sphinx |
|
707 | 707 | * :ghpull:`3082`: A few small patches to image handling |
|
708 | 708 | * :ghpull:`3078`: fix regular expression for detecting links in stdout |
|
709 | 709 | * :ghpull:`3054`: restore default behavior for automatic cluster size |
|
710 | 710 | * :ghpull:`3073`: fix ipython usage text |
|
711 | 711 | * :ghpull:`3083`: fix DisplayMagics.html docstring |
|
712 | 712 | * :ghpull:`3080`: noted sub_channel being renamed to iopub_channel |
|
713 | 713 | * :ghpull:`3079`: actually use IPKernelApp.kernel_class |
|
714 | 714 | * :ghpull:`3076`: Improve notebook.js documentation |
|
715 | 715 | * :ghpull:`3063`: add missing `%%html` magic |
|
716 | 716 | * :ghpull:`3075`: check for SIGUSR1 before using it, closes #3074 |
|
717 | 717 | * :ghpull:`3051`: add width:100% to vbox for webkit / FF consistency |
|
718 | 718 | * :ghpull:`2999`: increase registration timeout |
|
719 | 719 | * :ghpull:`2997`: fix DictDB default size limit |
|
720 | 720 | * :ghpull:`3033`: on resume, print server info again |
|
721 | 721 | * :ghpull:`3062`: test double pyximport |
|
722 | 722 | * :ghpull:`3046`: cast kernel cwd to bytes on Python 2 on Windows |
|
723 | 723 | * :ghpull:`3038`: remove xml from notebook magic docstrings |
|
724 | 724 | * :ghpull:`3032`: fix time format to international time format |
|
725 | 725 | * :ghpull:`3022`: Fix test for Windows |
|
726 | 726 | * :ghpull:`3024`: changed instances of 'outout' to 'output' in alt texts |
|
727 | 727 | * :ghpull:`3013`: py3 workaround for reload in cythonmagic |
|
728 | 728 | * :ghpull:`2961`: time magic: shorten unnecessary output on windows |
|
729 | 729 | * :ghpull:`2987`: fix local files examples in markdown |
|
730 | 730 | * :ghpull:`2998`: fix css in .output_area pre |
|
731 | 731 | * :ghpull:`3003`: add $include /etc/inputrc to suggested ~/.inputrc |
|
732 | 732 | * :ghpull:`2957`: Refactor qt import logic. Fixes #2955 |
|
733 | 733 | * :ghpull:`2994`: expanduser on %%file targets |
|
734 | 734 | * :ghpull:`2983`: fix run-all (that-> this) |
|
735 | 735 | * :ghpull:`2964`: fix count when testing composite error output |
|
736 | 736 | * :ghpull:`2967`: shows entire session history when only startsess is given |
|
737 | 737 | * :ghpull:`2942`: Move CM IPython theme out of codemirror folder |
|
738 | 738 | * :ghpull:`2929`: Cleanup cell insertion |
|
739 | 739 | * :ghpull:`2933`: Minordocupdate |
|
740 | 740 | * :ghpull:`2968`: fix notebook deletion. |
|
741 | 741 | * :ghpull:`2966`: Added assert msg to extract_hist_ranges() |
|
742 | 742 | * :ghpull:`2959`: Add command to trim the history database. |
|
743 | 743 | * :ghpull:`2681`: Don't enable pylab mode, when matplotlib is not importable |
|
744 | 744 | * :ghpull:`2901`: Fix inputhook_wx on osx |
|
745 | 745 | * :ghpull:`2871`: truncate potentially long CompositeErrors |
|
746 | 746 | * :ghpull:`2951`: use istype on lists/tuples |
|
747 | 747 | * :ghpull:`2946`: fix qtconsole history logic for end-of-line |
|
748 | 748 | * :ghpull:`2954`: fix logic for append_javascript |
|
749 | 749 | * :ghpull:`2941`: fix baseUrl |
|
750 | 750 | * :ghpull:`2903`: Specify toggle value on cell line number |
|
751 | 751 | * :ghpull:`2911`: display order in output area configurable |
|
752 | 752 | * :ghpull:`2897`: Dont rely on BaseProjectUrl data in body tag |
|
753 | 753 | * :ghpull:`2894`: Cm configurable |
|
754 | 754 | * :ghpull:`2927`: next release will be 1.0 |
|
755 | 755 | * :ghpull:`2932`: Simplify using notebook static files from external code |
|
756 | 756 | * :ghpull:`2915`: added small config section to notebook docs page |
|
757 | 757 | * :ghpull:`2924`: safe_run_module: Silence SystemExit codes 0 and None. |
|
758 | 758 | * :ghpull:`2906`: Unpatch/Monkey patch CM |
|
759 | 759 | * :ghpull:`2921`: add menu item for undo delete cell |
|
760 | 760 | * :ghpull:`2917`: Don't add logging handler if one already exists. |
|
761 | 761 | * :ghpull:`2910`: Respect DB_IP and DB_PORT in mongodb tests |
|
762 | 762 | * :ghpull:`2926`: Don't die if stderr/stdout do not support set_parent() #2925 |
|
763 | 763 | * :ghpull:`2885`: get monospace pager back |
|
764 | 764 | * :ghpull:`2876`: fix celltoolbar layout on FF |
|
765 | 765 | * :ghpull:`2904`: Skip remaining IPC test on Windows |
|
766 | 766 | * :ghpull:`2908`: fix last remaining KernelApp reference |
|
767 | 767 | * :ghpull:`2905`: fix a few remaining KernelApp/IPKernelApp changes |
|
768 | 768 | * :ghpull:`2900`: Don't assume test case for %time will finish in 0 time |
|
769 | 769 | * :ghpull:`2893`: exclude fabfile from tests |
|
770 | 770 | * :ghpull:`2884`: Correct import for kernelmanager on Windows |
|
771 | 771 | * :ghpull:`2882`: Utils cleanup |
|
772 | 772 | * :ghpull:`2883`: Don't call ast.fix_missing_locations unless the AST could have been modified |
|
773 | 773 | * :ghpull:`2855`: time(it) magic: Implement minutes/hour formatting and "%%time" cell magic |
|
774 | 774 | * :ghpull:`2874`: Empty cell warnings |
|
775 | 775 | * :ghpull:`2819`: tweak history prefix search (up/^p) in qtconsole |
|
776 | 776 | * :ghpull:`2868`: Import performance |
|
777 | 777 | * :ghpull:`2877`: minor css fixes |
|
778 | 778 | * :ghpull:`2880`: update examples docs with kernel move |
|
779 | 779 | * :ghpull:`2878`: Pass host environment on to kernel |
|
780 | 780 | * :ghpull:`2599`: func_kw_complete for builtin and cython with embededsignature=True using docstring |
|
781 | 781 | * :ghpull:`2792`: Add key "unique" to history_request protocol |
|
782 | 782 | * :ghpull:`2872`: fix payload keys |
|
783 | 783 | * :ghpull:`2869`: Fixing styling of toolbar selects on FF. |
|
784 | 784 | * :ghpull:`2708`: Less css |
|
785 | 785 | * :ghpull:`2854`: Move kernel code into IPython.kernel |
|
786 | 786 | * :ghpull:`2864`: Fix %run -t -N<N> TypeError |
|
787 | 787 | * :ghpull:`2852`: future pyzmq compatibility |
|
788 | 788 | * :ghpull:`2863`: whatsnew/version0.9.txt: Fix '~./ipython' -> '~/.ipython' typo |
|
789 | 789 | * :ghpull:`2861`: add missing KernelManager to ConsoleApp class list |
|
790 | 790 | * :ghpull:`2850`: Consolidate host IP detection in utils.localinterfaces |
|
791 | 791 | * :ghpull:`2859`: Correct docstring of ipython.py |
|
792 | 792 | * :ghpull:`2831`: avoid string version comparisons in external.qt |
|
793 | 793 | * :ghpull:`2844`: this should address the failure in #2732 |
|
794 | 794 | * :ghpull:`2849`: utils/data: Use list comprehension for uniq_stable() |
|
795 | 795 | * :ghpull:`2839`: add jinja to install docs / setup.py |
|
796 | 796 | * :ghpull:`2841`: Miscellaneous docs fixes |
|
797 | 797 | * :ghpull:`2811`: Still more KernelManager cleanup |
|
798 | 798 | * :ghpull:`2820`: add '=' to greedy completer delims |
|
799 | 799 | * :ghpull:`2818`: log user tracebacks in the kernel (INFO-level) |
|
800 | 800 | * :ghpull:`2828`: Clean up notebook Javascript |
|
801 | 801 | * :ghpull:`2829`: avoid comparison error in dictdb hub history |
|
802 | 802 | * :ghpull:`2830`: BUG: Opening parenthesis after non-callable raises ValueError |
|
803 | 803 | * :ghpull:`2718`: try to fallback to pysqlite2.dbapi2 as sqlite3 in core.history |
|
804 | 804 | * :ghpull:`2816`: in %edit, don't save "last_call" unless last call succeeded |
|
805 | 805 | * :ghpull:`2817`: change ol format order |
|
806 | 806 | * :ghpull:`2537`: Organize example notebooks |
|
807 | 807 | * :ghpull:`2815`: update release/authors |
|
808 | 808 | * :ghpull:`2808`: improve patience for slow Hub in client tests |
|
809 | 809 | * :ghpull:`2812`: remove nonfunctional `-la` short arg in cython magic |
|
810 | 810 | * :ghpull:`2810`: remove dead utils.upgradedir |
|
811 | 811 | * :ghpull:`1671`: __future__ environments |
|
812 | 812 | * :ghpull:`2804`: skip ipc tests on Windows |
|
813 | 813 | * :ghpull:`2789`: Fixing styling issues with CellToolbar. |
|
814 | 814 | * :ghpull:`2805`: fix KeyError creating ZMQStreams in notebook |
|
815 | 815 | * :ghpull:`2775`: General cleanup of kernel manager code. |
|
816 | 816 | * :ghpull:`2340`: Initial Code to reduce parallel.Client caching |
|
817 | 817 | * :ghpull:`2799`: Exit code |
|
818 | 818 | * :ghpull:`2800`: use `type(obj) is cls` as switch when canning |
|
819 | 819 | * :ghpull:`2801`: Fix a breakpoint bug |
|
820 | 820 | * :ghpull:`2795`: Remove outdated code from extensions.autoreload |
|
821 | 821 | * :ghpull:`2796`: P3K: fix cookie parsing under Python 3.x (+ duplicate import is removed) |
|
822 | 822 | * :ghpull:`2724`: In-process kernel support (take 3) |
|
823 | 823 | * :ghpull:`2687`: [WIP] Metaui slideshow |
|
824 | 824 | * :ghpull:`2788`: Chrome frame awareness |
|
825 | 825 | * :ghpull:`2649`: Add version_request/reply messaging protocol |
|
826 | 826 | * :ghpull:`2753`: add `%%px --local` for local execution |
|
827 | 827 | * :ghpull:`2783`: Prefilter shouldn't touch execution_count |
|
828 | 828 | * :ghpull:`2333`: UI For Metadata |
|
829 | 829 | * :ghpull:`2396`: create a ipynbv3 json schema and a validator |
|
830 | 830 | * :ghpull:`2757`: check for complete pyside presence before trying to import |
|
831 | 831 | * :ghpull:`2782`: Allow the %run magic with '-b' to specify a file. |
|
832 | 832 | * :ghpull:`2778`: P3K: fix DeprecationWarning under Python 3.x |
|
833 | 833 | * :ghpull:`2776`: remove non-functional View.kill method |
|
834 | 834 | * :ghpull:`2755`: can interactively defined classes |
|
835 | 835 | * :ghpull:`2774`: Removing unused code in the notebook MappingKernelManager. |
|
836 | 836 | * :ghpull:`2773`: Fixed minor typo causing AttributeError to be thrown. |
|
837 | 837 | * :ghpull:`2609`: Add 'unique' option to history_request messaging protocol |
|
838 | 838 | * :ghpull:`2769`: Allow shutdown when no engines are registered |
|
839 | 839 | * :ghpull:`2766`: Define __file__ when we %edit a real file. |
|
840 | 840 | * :ghpull:`2476`: allow %edit <variable> to work when interactively defined |
|
841 | 841 | * :ghpull:`2763`: Reset readline delimiters after loading rmagic. |
|
842 | 842 | * :ghpull:`2460`: Better handling of `__file__` when running scripts. |
|
843 | 843 | * :ghpull:`2617`: Fix for `units` argument. Adds a `res` argument. |
|
844 | 844 | * :ghpull:`2738`: Unicode content crashes the pager (console) |
|
845 | 845 | * :ghpull:`2749`: Tell Travis CI to test on Python 3.3 as well |
|
846 | 846 | * :ghpull:`2744`: Don't show 'try %paste' message while using magics |
|
847 | 847 | * :ghpull:`2728`: shift tab for tooltip |
|
848 | 848 | * :ghpull:`2741`: Add note to `%cython` Black-Scholes example warning of missing erf. |
|
849 | 849 | * :ghpull:`2743`: BUG: Octavemagic inline plots not working on Windows: Fixed |
|
850 | 850 | * :ghpull:`2740`: Following #2737 this error is now a name error |
|
851 | 851 | * :ghpull:`2737`: Rmagic: error message when moving an non-existant variable from python to R |
|
852 | 852 | * :ghpull:`2723`: diverse fixes for project url |
|
853 | 853 | * :ghpull:`2731`: %Rpush: Look for variables in the local scope first. |
|
854 | 854 | * :ghpull:`2544`: Infinite loop when multiple debuggers have been attached. |
|
855 | 855 | * :ghpull:`2726`: Add qthelp docs creation |
|
856 | 856 | * :ghpull:`2730`: added blockquote CSS |
|
857 | 857 | * :ghpull:`2729`: Fix Read the doc build, Again |
|
858 | 858 | * :ghpull:`2446`: [alternate 2267] Offline mathjax |
|
859 | 859 | * :ghpull:`2716`: remove unexisting headings level |
|
860 | 860 | * :ghpull:`2717`: One liner to fix debugger printing stack traces when lines of context are larger than source. |
|
861 | 861 | * :ghpull:`2713`: Doc bugfix: user_ns is not an attribute of Magic objects. |
|
862 | 862 | * :ghpull:`2690`: Fix 'import '... completion for py3 & egg files. |
|
863 | 863 | * :ghpull:`2691`: Document OpenMP in %%cython magic |
|
864 | 864 | * :ghpull:`2699`: fix jinja2 rendering for password protected notebooks |
|
865 | 865 | * :ghpull:`2700`: Skip notebook testing if jinja2 is not available. |
|
866 | 866 | * :ghpull:`2692`: Add %%cython magics to generated documentation. |
|
867 | 867 | * :ghpull:`2685`: Fix pretty print of types when `__module__` is not available. |
|
868 | 868 | * :ghpull:`2686`: Fix tox.ini |
|
869 | 869 | * :ghpull:`2604`: Backslashes are misinterpreted as escape-sequences by the R-interpreter. |
|
870 | 870 | * :ghpull:`2689`: fix error in doc (arg->kwarg) and pep-8 |
|
871 | 871 | * :ghpull:`2683`: for downloads, replaced window.open with window.location.assign |
|
872 | 872 | * :ghpull:`2659`: small bugs in js are fixed |
|
873 | 873 | * :ghpull:`2363`: Refactor notebook templates to use Jinja2 |
|
874 | 874 | * :ghpull:`2662`: qtconsole: wrap argument list in tooltip to match width of text body |
|
875 | 875 | * :ghpull:`2328`: addition of classes to generate a link or list of links from files local to the IPython HTML notebook |
|
876 | 876 | * :ghpull:`2668`: pylab_not_importable: Catch all exceptions, not just RuntimeErrors. |
|
877 | 877 | * :ghpull:`2663`: Fix issue #2660: parsing of help and version arguments |
|
878 | 878 | * :ghpull:`2656`: Fix irunner tests when $PYTHONSTARTUP is set |
|
879 | 879 | * :ghpull:`2312`: Add bracket matching to code cells in notebook |
|
880 | 880 | * :ghpull:`2571`: Start to document Javascript |
|
881 | 881 | * :ghpull:`2641`: undefinied that -> this |
|
882 | 882 | * :ghpull:`2638`: Fix %paste in Python 3 on Mac |
|
883 | 883 | * :ghpull:`2301`: Ast transfomers |
|
884 | 884 | * :ghpull:`2616`: Revamp API docs |
|
885 | 885 | * :ghpull:`2572`: Make 'Paste Above' the default paste behavior. |
|
886 | 886 | * :ghpull:`2574`: Fix #2244 |
|
887 | 887 | * :ghpull:`2582`: Fix displaying history when output cache is disabled. |
|
888 | 888 | * :ghpull:`2591`: Fix for Issue #2584 |
|
889 | 889 | * :ghpull:`2526`: Don't kill paramiko tunnels when receiving ^C |
|
890 | 890 | * :ghpull:`2559`: Add psource, pfile, pinfo2 commands to ipdb. |
|
891 | 891 | * :ghpull:`2546`: use 4 Pythons to build 4 Windows installers |
|
892 | 892 | * :ghpull:`2561`: Fix display of plain text containing multiple carriage returns before line feed |
|
893 | 893 | * :ghpull:`2549`: Add a simple 'undo' for cell deletion. |
|
894 | 894 | * :ghpull:`2525`: Add event to kernel execution/shell reply. |
|
895 | 895 | * :ghpull:`2554`: Avoid stopping in ipdb until we reach the main script. |
|
896 | 896 | * :ghpull:`2404`: Option to limit search result in history magic command |
|
897 | 897 | * :ghpull:`2294`: inputhook_qt4: Use QEventLoop instead of starting up the QCoreApplication |
|
898 | 898 | * :ghpull:`2233`: Refactored Drag and Drop Support in Qt Console |
|
899 | 899 | * :ghpull:`1747`: switch between hsplit and vsplit paging (request for feedback) |
|
900 | 900 | * :ghpull:`2530`: Adding time offsets to the video |
|
901 | 901 | * :ghpull:`2542`: Allow starting IPython as `python -m IPython`. |
|
902 | 902 | * :ghpull:`2534`: Do not unescape backslashes in Windows (shellglob) |
|
903 | 903 | * :ghpull:`2517`: Improved MathJax, bug fixes |
|
904 | 904 | * :ghpull:`2511`: trigger default remote_profile_dir when profile_dir is set |
|
905 | 905 | * :ghpull:`2491`: color is supported in ironpython |
|
906 | 906 | * :ghpull:`2462`: Track which extensions are loaded |
|
907 | 907 | * :ghpull:`2464`: Locate URLs in text output and convert them to hyperlinks. |
|
908 | 908 | * :ghpull:`2490`: add ZMQInteractiveShell to IPEngineApp class list |
|
909 | 909 | * :ghpull:`2498`: Don't catch tab press when something selected |
|
910 | 910 | * :ghpull:`2527`: Run All Above and Run All Below |
|
911 | 911 | * :ghpull:`2513`: add GitHub uploads to release script |
|
912 | 912 | * :ghpull:`2529`: Windows aware tests for shellglob |
|
913 | 913 | * :ghpull:`2478`: Fix doctest_run_option_parser for Windows |
|
914 | 914 | * :ghpull:`2519`: clear In[ ] prompt numbers again |
|
915 | 915 | * :ghpull:`2467`: Clickable links |
|
916 | 916 | * :ghpull:`2500`: Add `encoding` attribute to `OutStream` class. |
|
917 | 917 | * :ghpull:`2349`: ENH: added StackExchange-style MathJax filtering |
|
918 | 918 | * :ghpull:`2503`: Fix traceback handling of SyntaxErrors without line numbers. |
|
919 | 919 | * :ghpull:`2492`: add missing 'qtconsole' extras_require |
|
920 | 920 | * :ghpull:`2480`: Add deprecation warnings for sympyprinting |
|
921 | 921 | * :ghpull:`2334`: Make the ipengine monitor the ipcontroller heartbeat and die if the ipcontroller goes down |
|
922 | 922 | * :ghpull:`2479`: use new _winapi instead of removed _subprocess |
|
923 | 923 | * :ghpull:`2474`: fix bootstrap name conflicts |
|
924 | 924 | * :ghpull:`2469`: Treat __init__.pyc same as __init__.py in module_list |
|
925 | 925 | * :ghpull:`2165`: Add -g option to %run to glob expand arguments |
|
926 | 926 | * :ghpull:`2468`: Tell git to ignore __pycache__ directories. |
|
927 | 927 | * :ghpull:`2421`: Some notebook tweaks. |
|
928 | 928 | * :ghpull:`2291`: Remove old plugin system |
|
929 | 929 | * :ghpull:`2127`: Ability to build toolbar in JS |
|
930 | 930 | * :ghpull:`2445`: changes for ironpython |
|
931 | 931 | * :ghpull:`2420`: Pass ipython_dir to __init__() method of TerminalInteractiveShell's superclass. |
|
932 | 932 | * :ghpull:`2432`: Revert #1831, the `__file__` injection in safe_execfile / safe_execfile_ipy. |
|
933 | 933 | * :ghpull:`2216`: Autochange highlight with cell magics |
|
934 | 934 | * :ghpull:`1946`: Add image message handler in ZMQTerminalInteractiveShell |
|
935 | 935 | * :ghpull:`2424`: skip find_cmd when setting up script magics |
|
936 | 936 | * :ghpull:`2389`: Catch sqlite DatabaseErrors in more places when reading the history database |
|
937 | 937 | * :ghpull:`2395`: Don't catch ImportError when trying to unpack module functions |
|
938 | 938 | * :ghpull:`1868`: enable IPC transport for kernels |
|
939 | 939 | * :ghpull:`2437`: don't let log cleanup prevent engine start |
|
940 | 940 | * :ghpull:`2441`: `sys.maxsize` is the maximum length of a container. |
|
941 | 941 | * :ghpull:`2442`: allow iptest to be interrupted |
|
942 | 942 | * :ghpull:`2240`: fix message built for engine dying during task |
|
943 | 943 | * :ghpull:`2369`: Block until kernel termination after sending a kill signal |
|
944 | 944 | * :ghpull:`2439`: Py3k: Octal (0777 -> 0o777) |
|
945 | 945 | * :ghpull:`2326`: Detachable pager in notebook. |
|
946 | 946 | * :ghpull:`2377`: Fix installation of man pages in Python 3 |
|
947 | 947 | * :ghpull:`2407`: add IPython version to message headers |
|
948 | 948 | * :ghpull:`2408`: Fix Issue #2366 |
|
949 | 949 | * :ghpull:`2405`: clarify TaskScheduler.hwm doc |
|
950 | 950 | * :ghpull:`2399`: IndentationError display |
|
951 | 951 | * :ghpull:`2400`: Add scroll_to_cell(cell_number) to the notebook |
|
952 | 952 | * :ghpull:`2401`: unmock read-the-docs modules |
|
953 | 953 | * :ghpull:`2311`: always perform requested trait assignments |
|
954 | 954 | * :ghpull:`2393`: New option `n` to limit history search hits |
|
955 | 955 | * :ghpull:`2386`: Adapt inline backend to changes in matplotlib |
|
956 | 956 | * :ghpull:`2392`: Remove suspicious double quote |
|
957 | 957 | * :ghpull:`2387`: Added -L library search path to cythonmagic cell magic |
|
958 | 958 | * :ghpull:`2370`: qtconsole: Create a prompt newline by inserting a new block (w/o formatting) |
|
959 | 959 | * :ghpull:`1715`: Fix for #1688, traceback-unicode issue |
|
960 | 960 | * :ghpull:`2378`: use Singleton.instance() for embed() instead of manual global |
|
961 | 961 | * :ghpull:`2373`: fix missing imports in core.interactiveshell |
|
962 | 962 | * :ghpull:`2368`: remove notification widget leftover |
|
963 | 963 | * :ghpull:`2327`: Parallel: Support get/set of nested objects in view (e.g. dv['a.b']) |
|
964 | 964 | * :ghpull:`2362`: Clean up ProgressBar class in example notebook |
|
965 | 965 | * :ghpull:`2346`: Extra xterm identification in set_term_title |
|
966 | 966 | * :ghpull:`2352`: Notebook: Store the username in a cookie whose name is unique. |
|
967 | 967 | * :ghpull:`2358`: add backport_pr to tools |
|
968 | 968 | * :ghpull:`2365`: fix names of notebooks for download/save |
|
969 | 969 | * :ghpull:`2364`: make clients use 'location' properly (fixes #2361) |
|
970 | 970 | * :ghpull:`2354`: Refactor notebook templates to use Jinja2 |
|
971 | 971 | * :ghpull:`2339`: add bash completion example |
|
972 | 972 | * :ghpull:`2345`: Remove references to 'version' no longer in argparse. Github issue #2343. |
|
973 | 973 | * :ghpull:`2347`: adjust division error message checking to account for Python 3 |
|
974 | 974 | * :ghpull:`2305`: RemoteError._render_traceback_ calls self.render_traceback |
|
975 | 975 | * :ghpull:`2338`: Normalize line endings for ipexec_validate, fix for #2315. |
|
976 | 976 | * :ghpull:`2192`: Introduce Notification Area |
|
977 | 977 | * :ghpull:`2329`: Better error messages for common magic commands. |
|
978 | 978 | * :ghpull:`2337`: ENH: added StackExchange-style MathJax filtering |
|
979 | 979 | * :ghpull:`2331`: update css for qtconsole in doc |
|
980 | 980 | * :ghpull:`2317`: adding cluster_id to parallel.Client.__init__ |
|
981 | 981 | * :ghpull:`2130`: Add -l option to %R magic to allow passing in of local namespace |
|
982 | 982 | * :ghpull:`2196`: Fix for bad command line argument to latex |
|
983 | 983 | * :ghpull:`2300`: bug fix: was crashing when sqlite3 is not installed |
|
984 | 984 | * :ghpull:`2184`: Expose store_history to execute_request messages. |
|
985 | 985 | * :ghpull:`2308`: Add welcome_message option to enable_pylab |
|
986 | 986 | * :ghpull:`2302`: Fix variable expansion on 'self' |
|
987 | 987 | * :ghpull:`2299`: Remove code from prefilter that duplicates functionality in inputsplitter |
|
988 | 988 | * :ghpull:`2295`: allow pip install from github repository directly |
|
989 | 989 | * :ghpull:`2280`: fix SSH passwordless check for OpenSSH |
|
990 | 990 | * :ghpull:`2290`: nbmanager |
|
991 | 991 | * :ghpull:`2288`: s/assertEquals/assertEqual (again) |
|
992 | 992 | * :ghpull:`2287`: Removed outdated dev docs. |
|
993 | 993 | * :ghpull:`2218`: Use redirect for new notebooks |
|
994 | 994 | * :ghpull:`2277`: nb: up/down arrow keys move to begin/end of line at top/bottom of cell |
|
995 | 995 | * :ghpull:`2045`: Refactoring notebook managers and adding Azure backed storage. |
|
996 | 996 | * :ghpull:`2271`: use display instead of send_figure in inline backend hooks |
|
997 | 997 | * :ghpull:`2278`: allow disabling SQLite history |
|
998 | 998 | * :ghpull:`2225`: Add "--annotate" option to `%%cython` magic. |
|
999 | 999 | * :ghpull:`2246`: serialize individual args/kwargs rather than the containers |
|
1000 | 1000 | * :ghpull:`2274`: CLN: Use name to id mapping of notebooks instead of searching. |
|
1001 | 1001 | * :ghpull:`2270`: SSHLauncher tweaks |
|
1002 | 1002 | * :ghpull:`2269`: add missing location when disambiguating controller IP |
|
1003 | 1003 | * :ghpull:`2263`: Allow docs to build on http://readthedocs.org/ |
|
1004 | 1004 | * :ghpull:`2256`: Adding data publication example notebook. |
|
1005 | 1005 | * :ghpull:`2255`: better flush iopub with AsyncResults |
|
1006 | 1006 | * :ghpull:`2261`: Fix: longest_substr([]) -> '' |
|
1007 | 1007 | * :ghpull:`2260`: fix mpr again |
|
1008 | 1008 | * :ghpull:`2242`: Document globbing in `%history -g <pattern>`. |
|
1009 | 1009 | * :ghpull:`2250`: fix html in notebook example |
|
1010 | 1010 | * :ghpull:`2245`: Fix regression in embed() from pull-request #2096. |
|
1011 | 1011 | * :ghpull:`2248`: track sha of master in test_pr messages |
|
1012 | 1012 | * :ghpull:`2238`: Fast tests |
|
1013 | 1013 | * :ghpull:`2211`: add data publication message |
|
1014 | 1014 | * :ghpull:`2236`: minor test_pr tweaks |
|
1015 | 1015 | * :ghpull:`2231`: Improve Image format validation and add html width,height |
|
1016 | 1016 | * :ghpull:`2232`: Reapply monkeypatch to inspect.findsource() |
|
1017 | 1017 | * :ghpull:`2235`: remove spurious print statement from setupbase.py |
|
1018 | 1018 | * :ghpull:`2222`: adjust how canning deals with import strings |
|
1019 | 1019 | * :ghpull:`2224`: fix css typo |
|
1020 | 1020 | * :ghpull:`2223`: Custom tracebacks |
|
1021 | 1021 | * :ghpull:`2214`: use KernelApp.exec_lines/files in IPEngineApp |
|
1022 | 1022 | * :ghpull:`2199`: Wrap JS published by %%javascript in try/catch |
|
1023 | 1023 | * :ghpull:`2212`: catch errors in markdown javascript |
|
1024 | 1024 | * :ghpull:`2190`: Update code mirror 2.22 to 2.32 |
|
1025 | 1025 | * :ghpull:`2200`: documentation build broken in bb429da5b |
|
1026 | 1026 | * :ghpull:`2194`: clean nan/inf in json_clean |
|
1027 | 1027 | * :ghpull:`2198`: fix mpr for earlier git version |
|
1028 | 1028 | * :ghpull:`2175`: add FileFindHandler for Notebook static files |
|
1029 | 1029 | * :ghpull:`1990`: can func_defaults |
|
1030 | 1030 | * :ghpull:`2069`: start improving serialization in parallel code |
|
1031 | 1031 | * :ghpull:`2202`: Create a unique & temporary IPYTHONDIR for each testing group. |
|
1032 | 1032 | * :ghpull:`2204`: Work around lack of os.kill in win32. |
|
1033 | 1033 | * :ghpull:`2148`: win32 iptest: Use subprocess.Popen() instead of os.system(). |
|
1034 | 1034 | * :ghpull:`2179`: Pylab switch |
|
1035 | 1035 | * :ghpull:`2124`: Add an API for registering magic aliases. |
|
1036 | 1036 | * :ghpull:`2169`: ipdb: pdef, pdoc, pinfo magics all broken |
|
1037 | 1037 | * :ghpull:`2174`: Ensure consistent indentation in `%magic`. |
|
1038 | 1038 | * :ghpull:`1930`: add size-limiting to the DictDB backend |
|
1039 | 1039 | * :ghpull:`2189`: Fix IPython.lib.latextools for Python 3 |
|
1040 | 1040 | * :ghpull:`2186`: removed references to h5py dependence in octave magic documentation |
|
1041 | 1041 | * :ghpull:`2183`: Include the kernel object in the event object passed to kernel events |
|
1042 | 1042 | * :ghpull:`2185`: added test for %store, fixed storemagic |
|
1043 | 1043 | * :ghpull:`2138`: Use breqn.sty in dvipng backend if possible |
|
1044 | 1044 | * :ghpull:`2182`: handle undefined param in notebooklist |
|
1045 | 1045 | * :ghpull:`1831`: fix #1814 set __file__ when running .ipy files |
|
1046 | 1046 | * :ghpull:`2051`: Add a metadata attribute to messages |
|
1047 | 1047 | * :ghpull:`1471`: simplify IPython.parallel connections and enable Controller Resume |
|
1048 | 1048 | * :ghpull:`2181`: add %%javascript, %%svg, and %%latex display magics |
|
1049 | 1049 | * :ghpull:`2116`: different images in 00_notebook-tour |
|
1050 | 1050 | * :ghpull:`2092`: %prun: Restore `stats.stream` after running `print_stream`. |
|
1051 | 1051 | * :ghpull:`2159`: show message on notebook list if server is unreachable |
|
1052 | 1052 | * :ghpull:`2176`: fix git mpr |
|
1053 | 1053 | * :ghpull:`2152`: [qtconsole] Namespace not empty at startup |
|
1054 | 1054 | * :ghpull:`2177`: remove numpy install from travis/tox scripts |
|
1055 | 1055 | * :ghpull:`2090`: New keybinding for code cell execution + cell insertion |
|
1056 | 1056 | * :ghpull:`2160`: Updating the parallel options pricing example |
|
1057 | 1057 | * :ghpull:`2168`: expand line in cell magics |
|
1058 | 1058 | * :ghpull:`2170`: Fix tab completion with IPython.embed_kernel(). |
|
1059 | 1059 | * :ghpull:`2096`: embed(): Default to the future compiler flags of the calling frame. |
|
1060 | 1060 | * :ghpull:`2163`: fix 'remote_profie_dir' typo in SSH launchers |
|
1061 | 1061 | * :ghpull:`2158`: [2to3 compat ] Tuple params in func defs |
|
1062 | 1062 | * :ghpull:`2089`: Fix unittest DeprecationWarnings |
|
1063 | 1063 | * :ghpull:`2142`: Refactor test_pr.py |
|
1064 | 1064 | * :ghpull:`2140`: 2to3: Apply `has_key` fixer. |
|
1065 | 1065 | * :ghpull:`2131`: Add option append (-a) to %save |
|
1066 | 1066 | * :ghpull:`2117`: use explicit url in notebook example |
|
1067 | * :ghpull:`2133`: Tell git that *.py files contain Python code, for use in word-diffs. | |
|
1067 | * :ghpull:`2133`: Tell git that ``*.py`` files contain Python code, for use in word-diffs. | |
|
1068 | 1068 | * :ghpull:`2134`: Apply 2to3 `next` fix. |
|
1069 | 1069 | * :ghpull:`2126`: ipcluster broken with any batch launcher (PBS/LSF/SGE) |
|
1070 | 1070 | * :ghpull:`2104`: Windows make file for Sphinx documentation |
|
1071 | 1071 | * :ghpull:`2074`: Make BG color of inline plot configurable |
|
1072 | 1072 | * :ghpull:`2123`: BUG: Look up the `_repr_pretty_` method on the class within the MRO rath... |
|
1073 | 1073 | * :ghpull:`2100`: [in progress] python 2 and 3 compatibility without 2to3, second try |
|
1074 | 1074 | * :ghpull:`2128`: open notebook copy in different tabs |
|
1075 | 1075 | * :ghpull:`2073`: allows password and prefix for notebook |
|
1076 | 1076 | * :ghpull:`1993`: Print View |
|
1077 | 1077 | * :ghpull:`2086`: re-aliad %ed to %edit in qtconsole |
|
1078 | 1078 | * :ghpull:`2110`: Fixes and improvements to the input splitter |
|
1079 | 1079 | * :ghpull:`2101`: fix completer deletting newline |
|
1080 | 1080 | * :ghpull:`2102`: Fix logging on interactive shell. |
|
1081 | 1081 | * :ghpull:`2088`: Fix (some) Python 3.2 ResourceWarnings |
|
1082 | 1082 | * :ghpull:`2064`: conform to pep 3110 |
|
1083 | 1083 | * :ghpull:`2076`: Skip notebook 'static' dir in test suite. |
|
1084 | 1084 | * :ghpull:`2063`: Remove umlauts so py3 installations on LANG=C systems succeed. |
|
1085 | 1085 | * :ghpull:`2068`: record sysinfo in sdist |
|
1086 | 1086 | * :ghpull:`2067`: update tools/release_windows.py |
|
1087 | 1087 | * :ghpull:`2065`: Fix parentheses typo |
|
1088 | 1088 | * :ghpull:`2062`: Remove duplicates and auto-generated files from repo. |
|
1089 | 1089 | * :ghpull:`2061`: use explicit tuple in exception |
|
1090 | 1090 | * :ghpull:`2060`: change minus to \- or \(hy in manpages |
|
1091 | 1091 | |
|
1092 | 1092 | Issues (691): |
|
1093 | 1093 | |
|
1094 | 1094 | * :ghissue:`3940`: Install process documentation overhaul |
|
1095 | 1095 | * :ghissue:`3946`: The PDF option for `--post` should work with lowercase |
|
1096 | 1096 | * :ghissue:`3957`: Notebook help page broken in Firefox |
|
1097 | 1097 | * :ghissue:`3894`: nbconvert test failure |
|
1098 | 1098 | * :ghissue:`3887`: 1.0.0a1 shows blank screen in both firefox and chrome (windows 7) |
|
1099 | 1099 | * :ghissue:`3703`: `nbconvert`: Output options -- names and documentataion |
|
1100 | 1100 | * :ghissue:`3931`: Tab completion not working during debugging in the notebook |
|
1101 | 1101 | * :ghissue:`3936`: Ipcluster plugin is not working with Ipython 1.0dev |
|
1102 | 1102 | * :ghissue:`3941`: IPython Notebook kernel crash on Win7x64 |
|
1103 | 1103 | * :ghissue:`3926`: Ending Notebook renaming dialog with return creates new-line |
|
1104 | 1104 | * :ghissue:`3932`: Incorrect empty docstring |
|
1105 | 1105 | * :ghissue:`3928`: Passing variables to script from the workspace |
|
1106 | 1106 | * :ghissue:`3774`: Notebooks with spaces in their names breaks nbconvert latex graphics |
|
1107 | 1107 | * :ghissue:`3916`: tornado needs its own check |
|
1108 | 1108 | * :ghissue:`3915`: Link to Parallel examples "found on GitHub" broken in docs |
|
1109 | 1109 | * :ghissue:`3895`: Keyboard shortcuts box in notebook doesn't fit the screen |
|
1110 | 1110 | * :ghissue:`3912`: IPython.utils fails automated test for RC1 1.0.0 |
|
1111 | 1111 | * :ghissue:`3636`: Code cell missing highlight on load |
|
1112 | 1112 | * :ghissue:`3897`: under Windows, "ipython3 nbconvert "C:/blabla/first_try.ipynb" --to latex --post PDF" POST processing action fails because of a bad parameter |
|
1113 | 1113 | * :ghissue:`3900`: python3 install syntax errors (OS X 10.8.4) |
|
1114 | 1114 | * :ghissue:`3899`: nbconvert to latex fails on notebooks with spaces in file name |
|
1115 | 1115 | * :ghissue:`3881`: Temporary Working Directory Test Fails |
|
1116 | 1116 | * :ghissue:`2750`: A way to freeze code cells in the notebook |
|
1117 | 1117 | * :ghissue:`3893`: Resize Local Image Files in Notebook doesn't work |
|
1118 | 1118 | * :ghissue:`3823`: nbconvert on windows: tex and paths |
|
1119 | 1119 | * :ghissue:`3885`: under Windows, "ipython3 nbconvert "C:/blabla/first_try.ipynb" --to latex" write "\" instead of "/" to reference file path in the .tex file |
|
1120 | 1120 | * :ghissue:`3889`: test_qt fails due to assertion error 'qt4' != 'qt' |
|
1121 | 1121 | * :ghissue:`3890`: double post, disregard this issue |
|
1122 | 1122 | * :ghissue:`3689`: nbconvert, remaining tests |
|
1123 | 1123 | * :ghissue:`3874`: Up/Down keys don't work to "Search previous command history" (besides Ctrl-p/Ctrl-n) |
|
1124 | 1124 | * :ghissue:`3853`: CodeMirror locks up in the notebook |
|
1125 | 1125 | * :ghissue:`3862`: can only connect to an ipcluster started with v1.0.0-dev (master branch) using an older ipython (v0.13.2), but cannot connect using ipython (v1.0.0-dev) |
|
1126 | 1126 | * :ghissue:`3869`: custom css not working. |
|
1127 | 1127 | * :ghissue:`2960`: Keyboard shortcuts |
|
1128 | 1128 | * :ghissue:`3795`: ipcontroller process goes to 100% CPU, ignores connection requests |
|
1129 | 1129 | * :ghissue:`3553`: Ipython and pylab crashes in windows and canopy |
|
1130 | 1130 | * :ghissue:`3837`: Cannot set custom mathjax url, crash notebook server. |
|
1131 | 1131 | * :ghissue:`3808`: "Naming" releases ? |
|
1132 | 1132 | * :ghissue:`2431`: TypeError: must be string without null bytes, not str |
|
1133 | 1133 | * :ghissue:`3856`: `?` at end of comment causes line to execute |
|
1134 | 1134 | * :ghissue:`3731`: nbconvert: add logging for the different steps of nbconvert |
|
1135 | 1135 | * :ghissue:`3835`: Markdown cells do not render correctly when mathjax is disabled |
|
1136 | 1136 | * :ghissue:`3843`: nbconvert to rst: leftover "In[ ]" |
|
1137 | 1137 | * :ghissue:`3799`: nbconvert: Ability to specify name of output file |
|
1138 | 1138 | * :ghissue:`3726`: Document when IPython.start_ipython() should be used versus IPython.embed() |
|
1139 | 1139 | * :ghissue:`3778`: Add no more readonly view in what's new |
|
1140 | 1140 | * :ghissue:`3754`: No Print View in Notebook in 1.0dev |
|
1141 | 1141 | * :ghissue:`3798`: IPython 0.12.1 Crashes on autocompleting sqlalchemy.func.row_number properties |
|
1142 | 1142 | * :ghissue:`3811`: Opening notebook directly from the command line with multi-directory support installed |
|
1143 | 1143 | * :ghissue:`3775`: Annoying behavior when clicking on cell after execution (Ctrl+Enter) |
|
1144 | 1144 | * :ghissue:`3809`: Possible to add some bpython features? |
|
1145 | 1145 | * :ghissue:`3810`: Printing the contents of an image file messes up shell text |
|
1146 | 1146 | * :ghissue:`3702`: `nbconvert`: Default help message should be that of --help |
|
1147 | 1147 | * :ghissue:`3735`: Nbconvert 1.0.0a1 does not take into account the pdf extensions in graphs |
|
1148 | 1148 | * :ghissue:`3719`: Bad strftime format, for windows, in nbconvert exporter |
|
1149 | 1149 | * :ghissue:`3786`: Zmq errors appearing with `Ctrl-C` in console/qtconsole |
|
1150 | 1150 | * :ghissue:`3019`: disappearing scrollbar on tooltip in Chrome 24 on Ubuntu 12.04 |
|
1151 | 1151 | * :ghissue:`3785`: ipdb completely broken in Qt console |
|
1152 | 1152 | * :ghissue:`3796`: Document the meaning of milestone/issues-tags for users. |
|
1153 | 1153 | * :ghissue:`3788`: Do not auto show tooltip if docstring empty. |
|
1154 | 1154 | * :ghissue:`1366`: [Web page] No link to front page from documentation |
|
1155 | 1155 | * :ghissue:`3739`: nbconvert (to slideshow) misses some of the math in markdown cells |
|
1156 | 1156 | * :ghissue:`3768`: increase and make timeout configurable in console completion. |
|
1157 | 1157 | * :ghissue:`3724`: ipcluster only running on one cpu |
|
1158 | 1158 | * :ghissue:`1592`: better message for unsupported nbformat |
|
1159 | 1159 | * :ghissue:`2049`: Can not stop "ipython kernel" on windows |
|
1160 | 1160 | * :ghissue:`3757`: Need direct entry point to given notebook |
|
1161 | 1161 | * :ghissue:`3745`: ImportError: cannot import name check_linecache_ipython |
|
1162 | 1162 | * :ghissue:`3701`: `nbconvert`: Final output file should be in same directory as input file |
|
1163 | 1163 | * :ghissue:`3738`: history -o works but history with -n produces identical results |
|
1164 | 1164 | * :ghissue:`3740`: error when attempting to run 'make' in docs directory |
|
1165 | 1165 | * :ghissue:`3737`: ipython nbconvert crashes with ValueError: Invalid format string. |
|
1166 | 1166 | * :ghissue:`3730`: nbconvert: unhelpful error when pandoc isn't installed |
|
1167 | 1167 | * :ghissue:`3718`: markdown cell cursor misaligned in notebook |
|
1168 | 1168 | * :ghissue:`3710`: mutiple input fields for %debug in the notebook after resetting the kernel |
|
1169 | 1169 | * :ghissue:`3713`: PyCharm has problems with IPython working inside PyPy created by virtualenv |
|
1170 | 1170 | * :ghissue:`3712`: Code completion: Complete on dictionary keys |
|
1171 | 1171 | * :ghissue:`3680`: --pylab and --matplotlib flag |
|
1172 | 1172 | * :ghissue:`3698`: nbconvert: Unicode error with minus sign |
|
1173 | 1173 | * :ghissue:`3693`: nbconvert does not process SVGs into PDFs |
|
1174 | 1174 | * :ghissue:`3688`: nbconvert, figures not extracting with Python 3.x |
|
1175 | 1175 | * :ghissue:`3542`: note new dependencies in docs / setup.py |
|
1176 | 1176 | * :ghissue:`2556`: [pagedown] do not target_blank anchor link |
|
1177 | 1177 | * :ghissue:`3684`: bad message when %pylab fails due import *other* than matplotlib |
|
1178 | 1178 | * :ghissue:`3682`: ipython notebook pylab inline import_all=False |
|
1179 | 1179 | * :ghissue:`3596`: MathjaxUtils race condition? |
|
1180 | 1180 | * :ghissue:`1540`: Comment/uncomment selection in notebook |
|
1181 | 1181 | * :ghissue:`2702`: frozen setup: permission denied for default ipython_dir |
|
1182 | 1182 | * :ghissue:`3672`: allow_none on Number-like traits. |
|
1183 | 1183 | * :ghissue:`2411`: add CONTRIBUTING.md |
|
1184 | 1184 | * :ghissue:`481`: IPython terminal issue with Qt4Agg on XP SP3 |
|
1185 | 1185 | * :ghissue:`2664`: How to preserve user variables from import clashing? |
|
1186 | 1186 | * :ghissue:`3436`: enable_pylab(import_all=False) still imports np |
|
1187 | 1187 | * :ghissue:`2630`: lib.pylabtools.figsize : NameError when using Qt4Agg backend and %pylab magic. |
|
1188 | 1188 | * :ghissue:`3154`: Notebook: no event triggered when a Cell is created |
|
1189 | 1189 | * :ghissue:`3579`: Nbconvert: SVG are not transformed to PDF anymore |
|
1190 | 1190 | * :ghissue:`3604`: MathJax rendering problem in `%%latex` cell |
|
1191 | 1191 | * :ghissue:`3668`: AttributeError: 'BlockingKernelClient' object has no attribute 'started_channels' |
|
1192 | 1192 | * :ghissue:`3245`: SyntaxError: encoding declaration in Unicode string |
|
1193 | 1193 | * :ghissue:`3639`: %pylab inline in IPYTHON notebook throws "RuntimeError: Cannot activate multiple GUI eventloops" |
|
1194 | 1194 | * :ghissue:`3663`: frontend deprecation warnings |
|
1195 | 1195 | * :ghissue:`3661`: run -m not behaving like python -m |
|
1196 | 1196 | * :ghissue:`3597`: re-do PR #3531 - allow markdown in Header cell |
|
1197 | 1197 | * :ghissue:`3053`: Markdown in header cells is not rendered |
|
1198 | 1198 | * :ghissue:`3655`: IPython finding its way into pasted strings. |
|
1199 | 1199 | * :ghissue:`3620`: uncaught errors in HTML output |
|
1200 | 1200 | * :ghissue:`3646`: get_dict() error |
|
1201 | 1201 | * :ghissue:`3004`: `%load_ext rmagic` fails when legacy ipy_user_conf.py is installed (in ipython 0.13.1 / OSX 10.8) |
|
1202 | 1202 | * :ghissue:`3638`: setp() issue in ipython notebook with figure references |
|
1203 | 1203 | * :ghissue:`3634`: nbconvert reveal to pdf conversion ignores styling, prints only a single page. |
|
1204 | 1204 | * :ghissue:`1307`: Remove pyreadline workarounds, we now require pyreadline >= 1.7.1 |
|
1205 | 1205 | * :ghissue:`3316`: find_cmd test failure on Windows |
|
1206 | 1206 | * :ghissue:`3494`: input() in notebook doesn't work in Python 3 |
|
1207 | 1207 | * :ghissue:`3427`: Deprecate `$` as mathjax delimiter |
|
1208 | 1208 | * :ghissue:`3625`: Pager does not open from button |
|
1209 | 1209 | * :ghissue:`3149`: Miscellaneous small nbconvert feedback |
|
1210 | 1210 | * :ghissue:`3617`: 256 color escapes support |
|
1211 | 1211 | * :ghissue:`3609`: %pylab inline blows up for single process ipython |
|
1212 | 1212 | * :ghissue:`2934`: Publish the Interactive MPI Demo Notebook |
|
1213 | 1213 | * :ghissue:`3614`: ansi escapes broken in master (ls --color) |
|
1214 | 1214 | * :ghissue:`3610`: If you don't have markdown, python setup.py install says no pygments |
|
1215 | 1215 | * :ghissue:`3547`: %run modules clobber each other |
|
1216 | 1216 | * :ghissue:`3602`: import_item fails when one tries to use DottedObjectName instead of a string |
|
1217 | 1217 | * :ghissue:`3563`: Duplicate tab completions in the notebook |
|
1218 | 1218 | * :ghissue:`3599`: Problems trying to run IPython on python3 without installing... |
|
1219 | 1219 | * :ghissue:`2937`: too long completion in notebook |
|
1220 | 1220 | * :ghissue:`3479`: Write empty name for the notebooks |
|
1221 | 1221 | * :ghissue:`3505`: nbconvert: Failure in specifying user filter |
|
1222 | 1222 | * :ghissue:`1537`: think a bit about namespaces |
|
1223 | 1223 | * :ghissue:`3124`: Long multiline strings in Notebook |
|
1224 | 1224 | * :ghissue:`3464`: run -d message unclear |
|
1225 | 1225 | * :ghissue:`2706`: IPython 0.13.1 ignoring $PYTHONSTARTUP |
|
1226 | 1226 | * :ghissue:`3587`: LaTeX escaping bug in nbconvert when exporting to HTML |
|
1227 | 1227 | * :ghissue:`3213`: Long running notebook died with a coredump |
|
1228 | 1228 | * :ghissue:`3580`: Running ipython with pypy on windows |
|
1229 | 1229 | * :ghissue:`3573`: custom.js not working |
|
1230 | 1230 | * :ghissue:`3544`: IPython.lib test failure on Windows |
|
1231 | 1231 | * :ghissue:`3352`: Install Sphinx extensions |
|
1232 | 1232 | * :ghissue:`2971`: [notebook]user needs to press ctrl-c twice to stop notebook server should be put into terminal window |
|
1233 | 1233 | * :ghissue:`2413`: ipython3 qtconsole fails to install: ipython 0.13 has no such extra feature 'qtconsole' |
|
1234 | 1234 | * :ghissue:`2618`: documentation is incorrect for install process |
|
1235 | 1235 | * :ghissue:`2595`: mac 10.8 qtconsole export history |
|
1236 | 1236 | * :ghissue:`2586`: cannot store aliases |
|
1237 | 1237 | * :ghissue:`2714`: ipython qtconsole print unittest messages in console instead his own window. |
|
1238 | 1238 | * :ghissue:`2669`: cython magic failing to work with openmp. |
|
1239 | 1239 | * :ghissue:`3256`: Vagrant pandas instance of iPython Notebook does not respect additional plotting arguments |
|
1240 | 1240 | * :ghissue:`3010`: cython magic fail if cache dir is deleted while in session |
|
1241 | 1241 | * :ghissue:`2044`: prune unused names from parallel.error |
|
1242 | 1242 | * :ghissue:`1145`: Online help utility broken in QtConsole |
|
1243 | 1243 | * :ghissue:`3439`: Markdown links no longer open in new window (with change from pagedown to marked) |
|
1244 | 1244 | * :ghissue:`3476`: _margv for macros seems to be missing |
|
1245 | 1245 | * :ghissue:`3499`: Add reveal.js library (version 2.4.0) inside IPython |
|
1246 | 1246 | * :ghissue:`2771`: Wiki Migration to GitHub |
|
1247 | 1247 | * :ghissue:`2887`: ipcontroller purging some engines during connect |
|
1248 | 1248 | * :ghissue:`626`: Enable Resuming Controller |
|
1249 | 1249 | * :ghissue:`2824`: Kernel restarting after message "Kernel XXXX failed to respond to heartbeat" |
|
1250 | 1250 | * :ghissue:`2823`: %%cython magic gives ImportError: dlopen(long_file_name.so, 2): image not found |
|
1251 | 1251 | * :ghissue:`2891`: In IPython for Python 3, system site-packages comes before user site-packages |
|
1252 | 1252 | * :ghissue:`2928`: Add magic "watch" function (example) |
|
1253 | 1253 | * :ghissue:`2931`: Problem rendering pandas dataframe in Firefox for Windows |
|
1254 | 1254 | * :ghissue:`2939`: [notebook] Figure legend not shown in inline backend if ouside the box of the axes |
|
1255 | 1255 | * :ghissue:`2972`: [notebook] in Markdown mode, press Enter key at the end of <some http link>, the next line is indented unexpectly |
|
1256 | 1256 | * :ghissue:`3069`: Instructions for installing IPython notebook on Windows |
|
1257 | 1257 | * :ghissue:`3444`: Encoding problem: cannot use if user's name is not ascii? |
|
1258 | 1258 | * :ghissue:`3335`: Reenable bracket matching |
|
1259 | 1259 | * :ghissue:`3386`: Magic %paste not working in Python 3.3.2. TypeError: Type str doesn't support the buffer API |
|
1260 | 1260 | * :ghissue:`3543`: Exception shutting down kernel from notebook dashboard (0.13.1) |
|
1261 | 1261 | * :ghissue:`3549`: Codecell size changes with selection |
|
1262 | 1262 | * :ghissue:`3445`: Adding newlines in %%latex cell |
|
1263 | 1263 | * :ghissue:`3237`: [notebook] Can't close a notebook without errors |
|
1264 | 1264 | * :ghissue:`2916`: colon invokes auto(un)indent in markdown cells |
|
1265 | 1265 | * :ghissue:`2167`: Indent and dedent in htmlnotebook |
|
1266 | 1266 | * :ghissue:`3545`: Notebook save button icon not clear |
|
1267 | 1267 | * :ghissue:`3534`: nbconvert incompatible with Windows? |
|
1268 | 1268 | * :ghissue:`3489`: Update example notebook that raw_input is allowed |
|
1269 | 1269 | * :ghissue:`3396`: Notebook checkpoint time is displayed an hour out |
|
1270 | 1270 | * :ghissue:`3261`: Empty revert to checkpoint menu if no checkpoint... |
|
1271 | 1271 | * :ghissue:`2984`: "print" magic does not work in Python 3 |
|
1272 | 1272 | * :ghissue:`3524`: Issues with pyzmq and ipython on EPD update |
|
1273 | 1273 | * :ghissue:`2434`: %store magic not auto-restoring |
|
1274 | 1274 | * :ghissue:`2720`: base_url and static path |
|
1275 | 1275 | * :ghissue:`2234`: Update various low resolution graphics for retina displays |
|
1276 | 1276 | * :ghissue:`2842`: Remember passwords for pw-protected notebooks |
|
1277 | 1277 | * :ghissue:`3244`: qtconsole: ValueError('close_fds is not supported on Windows platforms if you redirect stdin/stdout/stderr',) |
|
1278 | 1278 | * :ghissue:`2215`: AsyncResult.wait(0) can hang waiting for the client to get results? |
|
1279 | 1279 | * :ghissue:`2268`: provide mean to retrieve static data path |
|
1280 | 1280 | * :ghissue:`1905`: Expose UI for worksheets within each notebook |
|
1281 | 1281 | * :ghissue:`2380`: Qt inputhook prevents modal dialog boxes from displaying |
|
1282 | 1282 | * :ghissue:`3185`: prettify on double // |
|
1283 | 1283 | * :ghissue:`2821`: Test failure: IPython.parallel.tests.test_client.test_resubmit_header |
|
1284 | 1284 | * :ghissue:`2475`: [Notebook] Line is deindented when typing eg a colon in markdown mode |
|
1285 | 1285 | * :ghissue:`2470`: Do not destroy valid notebooks |
|
1286 | 1286 | * :ghissue:`860`: Allow the standalone export of a notebook to HTML |
|
1287 | 1287 | * :ghissue:`2652`: notebook with qt backend crashes at save image location popup |
|
1288 | 1288 | * :ghissue:`1587`: Improve kernel restarting in the notebook |
|
1289 | 1289 | * :ghissue:`2710`: Saving a plot in Mac OS X backend crashes IPython |
|
1290 | 1290 | * :ghissue:`2596`: notebook "Last saved:" is misleading on file opening. |
|
1291 | 1291 | * :ghissue:`2671`: TypeError :NoneType when executed "ipython qtconsole" in windows console |
|
1292 | 1292 | * :ghissue:`2703`: Notebook scrolling breaks after pager is shown |
|
1293 | 1293 | * :ghissue:`2803`: KernelManager and KernelClient should be two separate objects |
|
1294 | 1294 | * :ghissue:`2693`: TerminalIPythonApp configuration fails without ipython_config.py |
|
1295 | 1295 | * :ghissue:`2531`: IPython 0.13.1 python 2 32-bit installer includes 64-bit ipython*.exe launchers in the scripts folder |
|
1296 | 1296 | * :ghissue:`2520`: Control-C kills port forwarding |
|
1297 | 1297 | * :ghissue:`2279`: Setting `__file__` to None breaks Mayavi import |
|
1298 | 1298 | * :ghissue:`2161`: When logged into notebook, long titles are incorrectly positioned |
|
1299 | 1299 | * :ghissue:`1292`: Notebook, Print view should not be editable... |
|
1300 | 1300 | * :ghissue:`1731`: test parallel launchers |
|
1301 | 1301 | * :ghissue:`3227`: Improve documentation of ipcontroller and possible BUG |
|
1302 | 1302 | * :ghissue:`2896`: IPController very unstable |
|
1303 | 1303 | * :ghissue:`3517`: documentation build broken in head |
|
1304 | 1304 | * :ghissue:`3522`: UnicodeDecodeError: 'ascii' codec can't decode byte on Pycharm on Windows |
|
1305 | 1305 | * :ghissue:`3448`: Please include MathJax fonts with IPython Notebook |
|
1306 | 1306 | * :ghissue:`3519`: IPython Parallel map mysteriously turns pandas Series into numpy ndarray |
|
1307 | 1307 | * :ghissue:`3345`: IPython embedded shells ask if I want to exit, but I set confirm_exit = False |
|
1308 | 1308 | * :ghissue:`3509`: IPython won't close without asking "Are you sure?" in Firefox |
|
1309 | 1309 | * :ghissue:`3471`: Notebook jinja2/markupsafe depedencies in manual |
|
1310 | 1310 | * :ghissue:`3502`: Notebook broken in master |
|
1311 | 1311 | * :ghissue:`3302`: autoreload does not work in ipython 0.13.x, python 3.3 |
|
1312 | 1312 | * :ghissue:`3475`: no warning when leaving/closing notebook on master without saved changes |
|
1313 | 1313 | * :ghissue:`3490`: No obvious feedback when kernel crashes |
|
1314 | 1314 | * :ghissue:`1912`: Move all autoreload tests to their own group |
|
1315 | 1315 | * :ghissue:`2577`: sh.py and ipython for python 3.3 |
|
1316 | 1316 | * :ghissue:`3467`: %magic doesn't work |
|
1317 | 1317 | * :ghissue:`3501`: Editing markdown cells that wrap has off-by-one errors in cursor positioning |
|
1318 | 1318 | * :ghissue:`3492`: IPython for Python3 |
|
1319 | 1319 | * :ghissue:`3474`: unexpected keyword argument to remove_kernel |
|
1320 | 1320 | * :ghissue:`2283`: TypeError when using '?' after a string in a %logstart session |
|
1321 | 1321 | * :ghissue:`2787`: rmagic and pandas DataFrame |
|
1322 | 1322 | * :ghissue:`2605`: Ellipsis literal triggers AttributeError |
|
1323 | 1323 | * :ghissue:`1179`: Test unicode source in pinfo |
|
1324 | 1324 | * :ghissue:`2055`: drop Python 3.1 support |
|
1325 | 1325 | * :ghissue:`2293`: IPEP 2: Input transformations |
|
1326 | 1326 | * :ghissue:`2790`: %paste and %cpaste not removing "..." lines |
|
1327 | 1327 | * :ghissue:`3480`: Testing fails because iptest.py cannot be found |
|
1328 | 1328 | * :ghissue:`2580`: will not run within PIL build directory |
|
1329 | 1329 | * :ghissue:`2797`: RMagic, Dataframe Conversion Problem |
|
1330 | 1330 | * :ghissue:`2838`: Empty lines disappear from triple-quoted literals. |
|
1331 | 1331 | * :ghissue:`3050`: Broken link on IPython.core.display page |
|
1332 | 1332 | * :ghissue:`3473`: Config not passed down to subcommands |
|
1333 | 1333 | * :ghissue:`3462`: Setting log_format in config file results in error (and no format changes) |
|
1334 | 1334 | * :ghissue:`3311`: Notebook (occasionally) not working on windows (Sophos AV) |
|
1335 | 1335 | * :ghissue:`3461`: Cursor positioning off by a character in auto-wrapped lines |
|
1336 | 1336 | * :ghissue:`3454`: _repr_html_ error |
|
1337 | 1337 | * :ghissue:`3457`: Space in long Paragraph Markdown cell with Chinese or Japanese |
|
1338 | 1338 | * :ghissue:`3447`: Run Cell Does not Work |
|
1339 | 1339 | * :ghissue:`1373`: Last lines in long cells are hidden |
|
1340 | 1340 | * :ghissue:`1504`: Revisit serialization in IPython.parallel |
|
1341 | 1341 | * :ghissue:`1459`: Can't connect to 2 HTTPS notebook servers on the same host |
|
1342 | 1342 | * :ghissue:`678`: Input prompt stripping broken with multiline data structures |
|
1343 | 1343 | * :ghissue:`3001`: IPython.notebook.dirty flag is not set when a cell has unsaved changes |
|
1344 | 1344 | * :ghissue:`3077`: Multiprocessing semantics in parallel.view.map |
|
1345 | 1345 | * :ghissue:`3056`: links across notebooks |
|
1346 | 1346 | * :ghissue:`3120`: Tornado 3.0 |
|
1347 | 1347 | * :ghissue:`3156`: update pretty to use Python 3 style for sets |
|
1348 | 1348 | * :ghissue:`3197`: Can't escape multiple dollar signs in a markdown cell |
|
1349 | 1349 | * :ghissue:`3309`: `Image()` signature/doc improvements |
|
1350 | 1350 | * :ghissue:`3415`: Bug in IPython/external/path/__init__.py |
|
1351 | 1351 | * :ghissue:`3446`: Feature suggestion: Download matplotlib figure to client browser |
|
1352 | 1352 | * :ghissue:`3295`: autoexported notebooks: only export explicitly marked cells |
|
1353 | 1353 | * :ghissue:`3442`: Notebook: Summary table extracted from markdown headers |
|
1354 | 1354 | * :ghissue:`3438`: Zooming notebook in chrome is broken in master |
|
1355 | 1355 | * :ghissue:`1378`: Implement autosave in notebook |
|
1356 | 1356 | * :ghissue:`3437`: Highlighting matching parentheses |
|
1357 | 1357 | * :ghissue:`3435`: module search segfault |
|
1358 | 1358 | * :ghissue:`3424`: ipcluster --version |
|
1359 | 1359 | * :ghissue:`3434`: 0.13.2 Ipython/genutils.py doesn't exist |
|
1360 | 1360 | * :ghissue:`3426`: Feature request: Save by cell and not by line #: IPython %save magic |
|
1361 | 1361 | * :ghissue:`3412`: Non Responsive Kernel: Running a Django development server from an IPython Notebook |
|
1362 | 1362 | * :ghissue:`3408`: Save cell toolbar and slide type metadata in notebooks |
|
1363 | 1363 | * :ghissue:`3246`: %paste regression with blank lines |
|
1364 | 1364 | * :ghissue:`3404`: Weird error with $variable and grep in command line magic (!command) |
|
1365 | 1365 | * :ghissue:`3405`: Key auto-completion in dictionaries? |
|
1366 | 1366 | * :ghissue:`3259`: Codemirror linenumber css broken |
|
1367 | 1367 | * :ghissue:`3397`: Vertical text misalignment in Markdown cells |
|
1368 | 1368 | * :ghissue:`3391`: Revert #3358 once fix integrated into CM |
|
1369 | 1369 | * :ghissue:`3360`: Error 500 while saving IPython notebook |
|
1370 | 1370 | * :ghissue:`3375`: Frequent Safari/Webkit crashes |
|
1371 | 1371 | * :ghissue:`3365`: zmq frontend |
|
1372 | 1372 | * :ghissue:`2654`: User_expression issues |
|
1373 | 1373 | * :ghissue:`3389`: Store history as plain text |
|
1374 | 1374 | * :ghissue:`3388`: Ipython parallel: open TCP connection created for each result returned from engine |
|
1375 | 1375 | * :ghissue:`3385`: setup.py failure on Python 3 |
|
1376 | 1376 | * :ghissue:`3376`: Setting `__module__` to None breaks pretty printing |
|
1377 | 1377 | * :ghissue:`3374`: ipython qtconsole does not display the prompt on OSX |
|
1378 | 1378 | * :ghissue:`3380`: simple call to kernel |
|
1379 | 1379 | * :ghissue:`3379`: TaskRecord key 'started' not set |
|
1380 | 1380 | * :ghissue:`3241`: notebook conection time out |
|
1381 | 1381 | * :ghissue:`3334`: magic interpreter interpretes non magic commands? |
|
1382 | 1382 | * :ghissue:`3326`: python3.3: Type error when launching SGE cluster in IPython notebook |
|
1383 | 1383 | * :ghissue:`3349`: pip3 doesn't run 2to3? |
|
1384 | 1384 | * :ghissue:`3347`: Longlist support in ipdb |
|
1385 | 1385 | * :ghissue:`3343`: Make pip install / easy_install faster |
|
1386 | 1386 | * :ghissue:`3337`: git submodules broke nightly PPA builds |
|
1387 | 1387 | * :ghissue:`3206`: Copy/Paste Regression in QtConsole |
|
1388 | 1388 | * :ghissue:`3329`: Buggy linewrap in Mac OSX Terminal (Mountain Lion) |
|
1389 | 1389 | * :ghissue:`3327`: Qt version check broken |
|
1390 | 1390 | * :ghissue:`3303`: parallel tasks never finish under heavy load |
|
1391 | 1391 | * :ghissue:`1381`: '\\' for equation continuations require an extra '\' in markdown cells |
|
1392 | 1392 | * :ghissue:`3314`: Error launching iPython |
|
1393 | 1393 | * :ghissue:`3306`: Test failure when running on a Vagrant VM |
|
1394 | 1394 | * :ghissue:`3280`: IPython.utils.process.getoutput returns stderr |
|
1395 | 1395 | * :ghissue:`3299`: variables named _ or __ exhibit incorrect behavior |
|
1396 | 1396 | * :ghissue:`3196`: add an "x" or similar to htmlnotebook pager |
|
1397 | 1397 | * :ghissue:`3293`: Several 404 errors for js files Firefox |
|
1398 | 1398 | * :ghissue:`3292`: syntax highlighting in chrome on OSX 10.8.3 |
|
1399 | 1399 | * :ghissue:`3288`: Latest dev version hangs on page load |
|
1400 | 1400 | * :ghissue:`3283`: ipython dev retains directory information after directory change |
|
1401 | 1401 | * :ghissue:`3279`: custom.css is not overridden in the dev IPython (1.0) |
|
1402 | 1402 | * :ghissue:`2727`: %run -m doesn't support relative imports |
|
1403 | 1403 | * :ghissue:`3268`: GFM triple backquote and unknown language |
|
1404 | 1404 | * :ghissue:`3273`: Suppressing all plot related outputs |
|
1405 | 1405 | * :ghissue:`3272`: Backspace while completing load previous page |
|
1406 | 1406 | * :ghissue:`3260`: Js error in savewidget |
|
1407 | 1407 | * :ghissue:`3247`: scrollbar in notebook when not needed? |
|
1408 | 1408 | * :ghissue:`3243`: notebook: option to view json source from browser |
|
1409 | 1409 | * :ghissue:`3265`: 404 errors when running IPython 1.0dev |
|
1410 | 1410 | * :ghissue:`3257`: setup.py not finding submodules |
|
1411 | 1411 | * :ghissue:`3253`: Incorrect Qt and PySide version comparison |
|
1412 | 1412 | * :ghissue:`3248`: Cell magics broken in Qt console |
|
1413 | 1413 | * :ghissue:`3012`: Problems with the less based style.min.css |
|
1414 | 1414 | * :ghissue:`2390`: Image width/height don't work in embedded images |
|
1415 | 1415 | * :ghissue:`3236`: cannot set TerminalIPythonApp.log_format |
|
1416 | 1416 | * :ghissue:`3214`: notebook kernel dies if started with invalid parameter |
|
1417 | 1417 | * :ghissue:`2980`: Remove HTMLCell ? |
|
1418 | 1418 | * :ghissue:`3128`: qtconsole hangs on importing pylab (using X forwarding) |
|
1419 | 1419 | * :ghissue:`3198`: Hitting recursive depth causing all notebook pages to hang |
|
1420 | 1420 | * :ghissue:`3218`: race conditions in profile directory creation |
|
1421 | 1421 | * :ghissue:`3177`: OverflowError execption in handlers.py |
|
1422 | 1422 | * :ghissue:`2563`: core.profiledir.check_startup_dir() doesn't work inside py2exe'd installation |
|
1423 | 1423 | * :ghissue:`3207`: [Feature] folders for ipython notebook dashboard |
|
1424 | 1424 | * :ghissue:`3178`: cell magics do not work with empty lines after #2447 |
|
1425 | 1425 | * :ghissue:`3204`: Default plot() colors unsuitable for red-green colorblind users |
|
1426 | * :ghissue:`1789`: :\n/*foo turns into :\n*(foo) in triple-quoted strings. | |
|
1426 | * :ghissue:`1789`: ``:\n/*foo`` turns into ``:\n*(foo)`` in triple-quoted strings. | |
|
1427 | 1427 | * :ghissue:`3202`: File cell magic fails with blank lines |
|
1428 | 1428 | * :ghissue:`3199`: %%cython -a stopped working? |
|
1429 | 1429 | * :ghissue:`2688`: obsolete imports in import autocompletion |
|
1430 | 1430 | * :ghissue:`3192`: Python2, Unhandled exception, __builtin__.True = False |
|
1431 | 1431 | * :ghissue:`3179`: script magic error message loop |
|
1432 | 1432 | * :ghissue:`3009`: use XDG_CACHE_HOME for cython objects |
|
1433 | 1433 | * :ghissue:`3059`: Bugs in 00_notebook_tour example. |
|
1434 | 1434 | * :ghissue:`3104`: Integrate a javascript file manager into the notebook front end |
|
1435 | 1435 | * :ghissue:`3176`: Particular equation not rendering (notebook) |
|
1436 | 1436 | * :ghissue:`1133`: [notebook] readonly and upload files/UI |
|
1437 | 1437 | * :ghissue:`2975`: [notebook] python file and cell toolbar |
|
1438 | 1438 | * :ghissue:`3017`: SciPy.weave broken in IPython notebook/ qtconsole |
|
1439 | 1439 | * :ghissue:`3161`: paste macro not reading spaces correctly |
|
1440 | 1440 | * :ghissue:`2835`: %paste not working on WinXpSP3/ipython-0.13.1.py2-win32-PROPER.exe/python27 |
|
1441 | 1441 | * :ghissue:`2628`: Make transformers work for lines following decorators |
|
1442 | 1442 | * :ghissue:`2612`: Multiline String containing ":\n?foo\n" confuses interpreter to replace ?foo with get_ipython().magic(u'pinfo foo') |
|
1443 | 1443 | * :ghissue:`2539`: Request: Enable cell magics inside of .ipy scripts |
|
1444 | 1444 | * :ghissue:`2507`: Multiline string does not work (includes `...`) with doctest type input in IPython notebook |
|
1445 | 1445 | * :ghissue:`2164`: Request: Line breaks in line magic command |
|
1446 | 1446 | * :ghissue:`3106`: poor parallel performance with many jobs |
|
1447 | 1447 | * :ghissue:`2438`: print inside multiprocessing crashes Ipython kernel |
|
1448 | 1448 | * :ghissue:`3155`: Bad md5 hash for package 0.13.2 |
|
1449 | 1449 | * :ghissue:`3045`: [Notebook] Ipython Kernel does not start if disconnected from internet(/network?) |
|
1450 | 1450 | * :ghissue:`3146`: Using celery in python 3.3 |
|
1451 | 1451 | * :ghissue:`3145`: The notebook viewer is down |
|
1452 | 1452 | * :ghissue:`2385`: grep --color not working well with notebook |
|
1453 | 1453 | * :ghissue:`3131`: Quickly install from source in a clean virtualenv? |
|
1454 | 1454 | * :ghissue:`3139`: Rolling log for ipython |
|
1455 | 1455 | * :ghissue:`3127`: notebook with pylab=inline appears to call figure.draw twice |
|
1456 | 1456 | * :ghissue:`3129`: Walking up and down the call stack |
|
1457 | 1457 | * :ghissue:`3123`: Notebook crashed if unplugged ethernet cable |
|
1458 | 1458 | * :ghissue:`3121`: NB should use normalize.css? was #3049 |
|
1459 | 1459 | * :ghissue:`3087`: Disable spellchecking in notebook |
|
1460 | 1460 | * :ghissue:`3084`: ipython pyqt 4.10 incompatibilty, QTextBlockUserData |
|
1461 | 1461 | * :ghissue:`3113`: Fails to install under Jython 2.7 beta |
|
1462 | 1462 | * :ghissue:`3110`: Render of h4 headers is not correct in notebook (error in renderedhtml.css) |
|
1463 | 1463 | * :ghissue:`3109`: BUG: read_csv: dtype={'id' : np.str}: Datatype not understood |
|
1464 | 1464 | * :ghissue:`3107`: Autocompletion of object attributes in arrays |
|
1465 | 1465 | * :ghissue:`3103`: Reset locale setting in qtconsole |
|
1466 | 1466 | * :ghissue:`3090`: python3.3 Entry Point not found |
|
1467 | 1467 | * :ghissue:`3081`: UnicodeDecodeError when using Image(data="some.jpeg") |
|
1468 | 1468 | * :ghissue:`2834`: url regexp only finds one link |
|
1469 | 1469 | * :ghissue:`3091`: qtconsole breaks doctest.testmod() in Python 3.3 |
|
1470 | 1470 | * :ghissue:`3074`: SIGUSR1 not available on Windows |
|
1471 | 1471 | * :ghissue:`2996`: registration::purging stalled registration high occurrence in small clusters |
|
1472 | 1472 | * :ghissue:`3065`: diff-ability of notebooks |
|
1473 | 1473 | * :ghissue:`3067`: Crash with pygit2 |
|
1474 | 1474 | * :ghissue:`3061`: Bug handling Ellipsis |
|
1475 | 1475 | * :ghissue:`3049`: NB css inconsistent behavior between ff and webkit |
|
1476 | 1476 | * :ghissue:`3039`: unicode errors when opening a new notebook |
|
1477 | 1477 | * :ghissue:`3048`: Installning ipython qtConsole should be easyer att Windows |
|
1478 | 1478 | * :ghissue:`3042`: Profile creation fails on 0.13.2 branch |
|
1479 | 1479 | * :ghissue:`3035`: docstring typo/inconsistency: mention of an xml notebook format? |
|
1480 | 1480 | * :ghissue:`3031`: HDF5 library segfault (possibly due to mismatching headers?) |
|
1481 | 1481 | * :ghissue:`2991`: In notebook importing sympy closes ipython kernel |
|
1482 | 1482 | * :ghissue:`3027`: f.__globals__ causes an error in Python 3.3 |
|
1483 | 1483 | * :ghissue:`3020`: Failing test test_interactiveshell.TestAstTransform on Windows |
|
1484 | 1484 | * :ghissue:`3023`: alt text for "click to expand output" has typo in alt text |
|
1485 | 1485 | * :ghissue:`2963`: %history to print all input history of a previous session when line range is omitted |
|
1486 | 1486 | * :ghissue:`3018`: IPython installed within virtualenv. WARNING "Please install IPython inside the virtualtenv" |
|
1487 | 1487 | * :ghissue:`2484`: Completion in Emacs *Python* buffer causes prompt to be increased. |
|
1488 | 1488 | * :ghissue:`3014`: Ctrl-C finishes notebook immediately |
|
1489 | 1489 | * :ghissue:`3007`: cython_pyximport reload broken in python3 |
|
1490 | 1490 | * :ghissue:`2955`: Incompatible Qt imports when running inprocess_qtconsole |
|
1491 | 1491 | * :ghissue:`3006`: [IPython 0.13.1] The check of PyQt version is wrong |
|
1492 | 1492 | * :ghissue:`3005`: Renaming a notebook to an existing notebook name overwrites the other file |
|
1493 | 1493 | * :ghissue:`2940`: Abort trap in IPython Notebook after installing matplotlib |
|
1494 | 1494 | * :ghissue:`3000`: issue #3000 |
|
1495 | 1495 | * :ghissue:`2995`: ipython_directive.py fails on multiline when prompt number < 100 |
|
1496 | 1496 | * :ghissue:`2993`: File magic (%%file) does not work with paths beginning with tilde (e.g., ~/anaconda/stuff.txt) |
|
1497 | 1497 | * :ghissue:`2992`: Cell-based input for console and qt frontends? |
|
1498 | 1498 | * :ghissue:`2425`: Liaise with Spyder devs to integrate newer IPython |
|
1499 | 1499 | * :ghissue:`2986`: requesting help in a loop can damage a notebook |
|
1500 | 1500 | * :ghissue:`2978`: v1.0-dev build errors on Arch with Python 3. |
|
1501 | 1501 | * :ghissue:`2557`: [refactor] Insert_cell_at_index() |
|
1502 | 1502 | * :ghissue:`2969`: ipython command does not work in terminal |
|
1503 | 1503 | * :ghissue:`2762`: OSX wxPython (osx_cocoa, 64bit) command "%gui wx" blocks the interpreter |
|
1504 | 1504 | * :ghissue:`2956`: Silent importing of submodules differs from standard Python3.2 interpreter's behavior |
|
1505 | 1505 | * :ghissue:`2943`: Up arrow key history search gets stuck in QTConsole |
|
1506 | 1506 | * :ghissue:`2953`: using 'nonlocal' declaration in global scope causes ipython3 crash |
|
1507 | 1507 | * :ghissue:`2952`: qtconsole ignores exec_lines |
|
1508 | 1508 | * :ghissue:`2949`: ipython crashes due to atexit() |
|
1509 | 1509 | * :ghissue:`2947`: From rmagic to an R console |
|
1510 | 1510 | * :ghissue:`2938`: docstring pane not showing in notebook |
|
1511 | 1511 | * :ghissue:`2936`: Tornado assumes invalid signature for parse_qs on Python 3.1 |
|
1512 | 1512 | * :ghissue:`2935`: unable to find python after easy_install / pip install |
|
1513 | 1513 | * :ghissue:`2920`: Add undo-cell deletion menu |
|
1514 | 1514 | * :ghissue:`2914`: BUG:saving a modified .py file after loading a module kills the kernel |
|
1515 | 1515 | * :ghissue:`2925`: BUG: kernel dies if user sets sys.stderr or sys.stdout to a file object |
|
1516 | 1516 | * :ghissue:`2909`: LaTeX sometimes fails to render in markdown cells with some curly bracket + underscore combinations |
|
1517 | 1517 | * :ghissue:`2898`: Skip ipc tests on Windows |
|
1518 | 1518 | * :ghissue:`2902`: ActiveState attempt to build ipython 0.12.1 for python 3.2.2 for Mac OS failed |
|
1519 | 1519 | * :ghissue:`2899`: Test failure in IPython.core.tests.test_magic.test_time |
|
1520 | 1520 | * :ghissue:`2890`: Test failure when fabric not installed |
|
1521 | 1521 | * :ghissue:`2892`: IPython tab completion bug for paths |
|
1522 | 1522 | * :ghissue:`1340`: Allow input cells to be collapsed |
|
1523 | 1523 | * :ghissue:`2881`: ? command in notebook does not show help in Safari |
|
1524 | 1524 | * :ghissue:`2751`: %%timeit should use minutes to format running time in long running cells |
|
1525 | 1525 | * :ghissue:`2879`: When importing a module with a wrong name, ipython crashes |
|
1526 | 1526 | * :ghissue:`2862`: %%timeit should warn of empty contents |
|
1527 | 1527 | * :ghissue:`2485`: History navigation breaks in qtconsole |
|
1528 | 1528 | * :ghissue:`2785`: gevent input hook |
|
1529 | 1529 | * :ghissue:`2843`: Sliently running code in clipboard (with paste, cpaste and variants) |
|
1530 | 1530 | * :ghissue:`2784`: %run -t -N<N> error |
|
1531 | 1531 | * :ghissue:`2732`: Test failure with FileLinks class on Windows |
|
1532 | 1532 | * :ghissue:`2860`: ipython help notebook -> KeyError: 'KernelManager' |
|
1533 | 1533 | * :ghissue:`2858`: Where is the installed `ipython` script? |
|
1534 | 1534 | * :ghissue:`2856`: Edit code entered from ipython in external editor |
|
1535 | 1535 | * :ghissue:`2722`: IPC transport option not taking effect ? |
|
1536 | 1536 | * :ghissue:`2473`: Better error messages in ipengine/ipcontroller |
|
1537 | 1537 | * :ghissue:`2836`: Cannot send builtin module definitions to IP engines |
|
1538 | 1538 | * :ghissue:`2833`: Any reason not to use super() ? |
|
1539 | 1539 | * :ghissue:`2781`: Cannot interrupt infinite loops in the notebook |
|
1540 | 1540 | * :ghissue:`2150`: clippath_demo.py in matplotlib example does not work with inline backend |
|
1541 | 1541 | * :ghissue:`2634`: Numbered list in notebook markdown cell renders with Roman numerals instead of numbers |
|
1542 | 1542 | * :ghissue:`2230`: IPython crashing during startup with "AttributeError: 'NoneType' object has no attribute 'rstrip'" |
|
1543 | 1543 | * :ghissue:`2483`: nbviewer bug? with multi-file gists |
|
1544 | 1544 | * :ghissue:`2466`: mistyping `ed -p` breaks `ed -p` |
|
1545 | 1545 | * :ghissue:`2477`: Glob expansion tests fail on Windows |
|
1546 | 1546 | * :ghissue:`2622`: doc issue: notebooks that ship with Ipython .13 are written for python 2.x |
|
1547 | 1547 | * :ghissue:`2626`: Add "Cell -> Run All Keep Going" for notebooks |
|
1548 | 1548 | * :ghissue:`1223`: Show last modification date of each notebook |
|
1549 | 1549 | * :ghissue:`2621`: user request: put link to example notebooks in Dashboard |
|
1550 | 1550 | * :ghissue:`2564`: grid blanks plots in ipython pylab inline mode (interactive) |
|
1551 | 1551 | * :ghissue:`2532`: Django shell (IPython) gives NameError on dict comprehensions |
|
1552 | 1552 | * :ghissue:`2188`: ipython crashes on ctrl-c |
|
1553 | 1553 | * :ghissue:`2391`: Request: nbformat API to load/save without changing version |
|
1554 | 1554 | * :ghissue:`2355`: Restart kernel message even though kernel is perfectly alive |
|
1555 | 1555 | * :ghissue:`2306`: Garbled input text after reverse search on Mac OS X |
|
1556 | 1556 | * :ghissue:`2297`: ipdb with separate kernel/client pushing stdout to kernel process only |
|
1557 | 1557 | * :ghissue:`2180`: Have [kernel busy] overridden only by [kernel idle] |
|
1558 | 1558 | * :ghissue:`1188`: Pylab with OSX backend keyboard focus issue and hang |
|
1559 | 1559 | * :ghissue:`2107`: test_octavemagic.py[everything] fails |
|
1560 | 1560 | * :ghissue:`1212`: Better understand/document browser compatibility |
|
1561 | 1561 | * :ghissue:`1585`: Refactor notebook templates to use Jinja2 and make each page a separate directory |
|
1562 | 1562 | * :ghissue:`1443`: xticks scaling factor partially obscured with qtconsole and inline plotting |
|
1563 | 1563 | * :ghissue:`1209`: can't make %result work as in doc. |
|
1564 | 1564 | * :ghissue:`1200`: IPython 0.12 Windows install fails on Vista |
|
1565 | 1565 | * :ghissue:`1127`: Interactive test scripts for Qt/nb issues |
|
1566 | 1566 | * :ghissue:`959`: Matplotlib figures hide |
|
1567 | 1567 | * :ghissue:`2071`: win32 installer issue on Windows XP |
|
1568 | 1568 | * :ghissue:`2610`: ZMQInteractiveShell.colors being ignored |
|
1569 | 1569 | * :ghissue:`2505`: Markdown Cell incorrectly highlighting after "<" |
|
1570 | 1570 | * :ghissue:`165`: Installer fails to create Start Menu entries on Windows |
|
1571 | 1571 | * :ghissue:`2356`: failing traceback in terminal ipython for first exception |
|
1572 | 1572 | * :ghissue:`2145`: Have dashboad show when server disconect |
|
1573 | 1573 | * :ghissue:`2098`: Do not crash on kernel shutdow if json file is missing |
|
1574 | 1574 | * :ghissue:`2813`: Offline MathJax is broken on 0.14dev |
|
1575 | 1575 | * :ghissue:`2807`: Test failure: IPython.parallel.tests.test_client.TestClient.test_purge_everything |
|
1576 | 1576 | * :ghissue:`2486`: Readline's history search in ipython console does not clear properly after cancellation with Ctrl+C |
|
1577 | 1577 | * :ghissue:`2709`: Cython -la doesn't work |
|
1578 | 1578 | * :ghissue:`2767`: What is IPython.utils.upgradedir ? |
|
1579 | 1579 | * :ghissue:`2210`: Placing matplotlib legend outside axis bounds causes inline display to clip it |
|
1580 | 1580 | * :ghissue:`2553`: IPython Notebooks not robust against client failures |
|
1581 | 1581 | * :ghissue:`2536`: ImageDraw in Ipython notebook not drawing lines |
|
1582 | 1582 | * :ghissue:`2264`: Feature request: Versioning messaging protocol |
|
1583 | 1583 | * :ghissue:`2589`: Creation of ~300+ MPI-spawned engines causes instability in ipcluster |
|
1584 | 1584 | * :ghissue:`2672`: notebook: inline option without pylab |
|
1585 | 1585 | * :ghissue:`2673`: Indefinite Articles & Traitlets |
|
1586 | 1586 | * :ghissue:`2705`: Notebook crashes Safari with select and drag |
|
1587 | 1587 | * :ghissue:`2721`: dreload kills ipython when it hits zmq |
|
1588 | 1588 | * :ghissue:`2806`: ipython.parallel doesn't discover globals under Python 3.3 |
|
1589 | 1589 | * :ghissue:`2794`: _exit_code behaves differently in terminal vs ZMQ frontends |
|
1590 | 1590 | * :ghissue:`2793`: IPython.parallel issue with pushing pandas TimeSeries |
|
1591 | 1591 | * :ghissue:`1085`: In process kernel for Qt frontend |
|
1592 | 1592 | * :ghissue:`2760`: IndexError: list index out of range with Python 3.2 |
|
1593 | 1593 | * :ghissue:`2780`: Save and load notebooks from github |
|
1594 | 1594 | * :ghissue:`2772`: AttributeError: 'Client' object has no attribute 'kill' |
|
1595 | 1595 | * :ghissue:`2754`: Fail to send class definitions from interactive session to engines namespaces |
|
1596 | 1596 | * :ghissue:`2764`: TypeError while using 'cd' |
|
1597 | 1597 | * :ghissue:`2765`: name '__file__' is not defined |
|
1598 | 1598 | * :ghissue:`2540`: Wrap tooltip if line exceeds threshold? |
|
1599 | 1599 | * :ghissue:`2394`: Startup error on ipython qtconsole (version 0.13 and 0.14-dev |
|
1600 | 1600 | * :ghissue:`2440`: IPEP 4: Python 3 Compatibility |
|
1601 | 1601 | * :ghissue:`1814`: __file__ is not defined when file end with .ipy |
|
1602 | 1602 | * :ghissue:`2759`: R magic extension interferes with tab completion |
|
1603 | 1603 | * :ghissue:`2615`: Small change needed to rmagic extension. |
|
1604 | 1604 | * :ghissue:`2748`: collapse parts of a html notebook |
|
1605 | 1605 | * :ghissue:`1661`: %paste still bugs about IndentationError and says to use %paste |
|
1606 | 1606 | * :ghissue:`2742`: Octavemagic fails to deliver inline images in IPython (on Windows) |
|
1607 | 1607 | * :ghissue:`2739`: wiki.ipython.org contaminated with prescription drug spam |
|
1608 | 1608 | * :ghissue:`2588`: Link error while executing code from cython example notebook |
|
1609 | 1609 | * :ghissue:`2550`: Rpush magic doesn't find local variables and doesn't support comma separated lists of variables |
|
1610 | 1610 | * :ghissue:`2675`: Markdown/html blockquote need css. |
|
1611 | 1611 | * :ghissue:`2419`: TerminalInteractiveShell.__init__() ignores value of ipython_dir argument |
|
1612 | 1612 | * :ghissue:`1523`: Better LaTeX printing in the qtconsole with the sympy profile |
|
1613 | 1613 | * :ghissue:`2719`: ipython fails with `pkg_resources.DistributionNotFound: ipython==0.13` |
|
1614 | 1614 | * :ghissue:`2715`: url crashes nbviewer.ipython.org |
|
1615 | 1615 | * :ghissue:`2555`: "import" module completion on MacOSX |
|
1616 | 1616 | * :ghissue:`2707`: Problem installing the new version of IPython in Windows |
|
1617 | 1617 | * :ghissue:`2696`: SymPy magic bug in IPython Notebook |
|
1618 | 1618 | * :ghissue:`2684`: pretty print broken for types created with PyType_FromSpec |
|
1619 | 1619 | * :ghissue:`2533`: rmagic breaks on Windows |
|
1620 | 1620 | * :ghissue:`2661`: Qtconsole tooltip is too wide when the function has many arguments |
|
1621 | 1621 | * :ghissue:`2679`: ipython3 qtconsole via Homebrew on Mac OS X 10.8 - pyqt/pyside import error |
|
1622 | 1622 | * :ghissue:`2646`: pylab_not_importable |
|
1623 | 1623 | * :ghissue:`2587`: cython magic pops 2 CLI windows upon execution on Windows |
|
1624 | 1624 | * :ghissue:`2660`: Certain arguments (-h, --help, --version) never passed to scripts run with ipython |
|
1625 | 1625 | * :ghissue:`2665`: Missing docs for rmagic and some other extensions |
|
1626 | 1626 | * :ghissue:`2611`: Travis wants to drop 3.1 support |
|
1627 | 1627 | * :ghissue:`2658`: Incorrect parsing of raw multiline strings |
|
1628 | 1628 | * :ghissue:`2655`: Test fails if `from __future__ import print_function` in .pythonrc.py |
|
1629 | 1629 | * :ghissue:`2651`: nonlocal with no existing variable produces too many errors |
|
1630 | 1630 | * :ghissue:`2645`: python3 is a pain (minor unicode bug) |
|
1631 | 1631 | * :ghissue:`2637`: %paste in Python 3 on Mac doesn't work |
|
1632 | 1632 | * :ghissue:`2624`: Error on launching IPython on Win 7 and Python 2.7.3 |
|
1633 | 1633 | * :ghissue:`2608`: disk IO activity on cursor press |
|
1634 | 1634 | * :ghissue:`1275`: Markdown parses LaTeX math symbols as its formatting syntax in notebook |
|
1635 | 1635 | * :ghissue:`2613`: display(Math(...)) doesn't render \tau correctly |
|
1636 | 1636 | * :ghissue:`925`: Tab-completion in Qt console needn't use pager |
|
1637 | 1637 | * :ghissue:`2607`: %load_ext sympy.interactive.ipythonprinting dammaging output |
|
1638 | 1638 | * :ghissue:`2593`: Toolbar button to open qtconsole from notebook |
|
1639 | 1639 | * :ghissue:`2602`: IPython html documentation for downloading |
|
1640 | 1640 | * :ghissue:`2598`: ipython notebook --pylab=inline replaces built-in any() |
|
1641 | 1641 | * :ghissue:`2244`: small issue: wrong printout |
|
1642 | 1642 | * :ghissue:`2590`: add easier way to execute scripts in the current directory |
|
1643 | 1643 | * :ghissue:`2581`: %hist does not work when InteractiveShell.cache_size = 0 |
|
1644 | 1644 | * :ghissue:`2584`: No file COPYING |
|
1645 | 1645 | * :ghissue:`2578`: AttributeError: 'module' object has no attribute 'TestCase' |
|
1646 | 1646 | * :ghissue:`2576`: One of my notebooks won't load any more -- is there a maximum notebook size? |
|
1647 | 1647 | * :ghissue:`2560`: Notebook output is invisible when printing strings with \r\r\n line endings |
|
1648 | 1648 | * :ghissue:`2566`: if pyside partially present ipython qtconsole fails to load even if pyqt4 present |
|
1649 | 1649 | * :ghissue:`1308`: ipython qtconsole --ssh=server --existing ... hangs |
|
1650 | 1650 | * :ghissue:`1679`: List command doesn't work in ipdb debugger the first time |
|
1651 | 1651 | * :ghissue:`2545`: pypi win32 installer creates 64bit executibles |
|
1652 | * :ghissue:`2080`: Event loop issues with IPython 0.12 and PyQt4 (QDialog.exec_ and more) | |
|
1652 | * :ghissue:`2080`: Event loop issues with IPython 0.12 and PyQt4 (``QDialog.exec_`` and more) | |
|
1653 | 1653 | * :ghissue:`2541`: Allow `python -m IPython` |
|
1654 | 1654 | * :ghissue:`2508`: subplots_adjust() does not work correctly in ipython notebook |
|
1655 | 1655 | * :ghissue:`2289`: Incorrect mathjax rendering of certain arrays of equations |
|
1656 | 1656 | * :ghissue:`2487`: Selecting and indenting |
|
1657 | 1657 | * :ghissue:`2521`: more fine-grained 'run' controls, such as 'run from here' and 'run until here' |
|
1658 | 1658 | * :ghissue:`2535`: Funny bounding box when plot with text |
|
1659 | 1659 | * :ghissue:`2523`: History not working |
|
1660 | 1660 | * :ghissue:`2514`: Issue with zooming in qtconsole |
|
1661 | 1661 | * :ghissue:`2220`: No sys.stdout.encoding in kernel based IPython |
|
1662 | 1662 | * :ghissue:`2512`: ERROR: Internal Python error in the inspect module. |
|
1663 | 1663 | * :ghissue:`2496`: Function passwd does not work in QtConsole |
|
1664 | 1664 | * :ghissue:`1453`: make engines reconnect/die when controller was restarted |
|
1665 | 1665 | * :ghissue:`2481`: ipython notebook -- clicking in a code cell's output moves the screen to the top of the code cell |
|
1666 | 1666 | * :ghissue:`2488`: Undesired plot outputs in Notebook inline mode |
|
1667 | 1667 | * :ghissue:`2482`: ipython notebook -- download may not get the latest notebook |
|
1668 | 1668 | * :ghissue:`2471`: _subprocess module removed in Python 3.3 |
|
1669 | 1669 | * :ghissue:`2374`: Issues with man pages |
|
1670 | 1670 | * :ghissue:`2316`: parallel.Client.__init__ should take cluster_id kwarg |
|
1671 | 1671 | * :ghissue:`2457`: Can a R library wrapper be created with Rmagic? |
|
1672 | 1672 | * :ghissue:`1575`: Fallback frontend for console when connecting pylab=inlnie -enabled kernel? |
|
1673 | 1673 | * :ghissue:`2097`: Do not crash if history db is corrupted |
|
1674 | 1674 | * :ghissue:`2435`: ipengines fail if clean_logs enabled |
|
1675 | 1675 | * :ghissue:`2429`: Using warnings.warn() results in TypeError |
|
1676 | 1676 | * :ghissue:`2422`: Multiprocessing in ipython notebook kernel crash |
|
1677 | 1677 | * :ghissue:`2426`: ipython crashes with the following message. I do not what went wrong. Can you help me identify the problem? |
|
1678 | 1678 | * :ghissue:`2423`: Docs typo? |
|
1679 | 1679 | * :ghissue:`2257`: pip install -e fails |
|
1680 | 1680 | * :ghissue:`2418`: rmagic can't run R's read.csv on data files with NA data |
|
1681 | 1681 | * :ghissue:`2417`: HTML notebook: Backspace sometimes deletes multiple characters |
|
1682 | 1682 | * :ghissue:`2275`: notebook: "Down_Arrow" on last line of cell should move to end of line |
|
1683 | 1683 | * :ghissue:`2414`: 0.13.1 does not work with current EPD 7.3-2 |
|
1684 | 1684 | * :ghissue:`2409`: there is a redundant None |
|
1685 | 1685 | * :ghissue:`2410`: Use /usr/bin/python3 instead of /usr/bin/python |
|
1686 | 1686 | * :ghissue:`2366`: Notebook Dashboard --notebook-dir and fullpath |
|
1687 | 1687 | * :ghissue:`2406`: Inability to get docstring in debugger |
|
1688 | 1688 | * :ghissue:`2398`: Show line number for IndentationErrors |
|
1689 | 1689 | * :ghissue:`2314`: HTML lists seem to interfere with the QtConsole display |
|
1690 | 1690 | * :ghissue:`1688`: unicode exception when using %run with failing script |
|
1691 | 1691 | * :ghissue:`1884`: IPython.embed changes color on error |
|
1692 | 1692 | * :ghissue:`2381`: %time doesn't work for multiline statements |
|
1693 | 1693 | * :ghissue:`1435`: Add size keywords in Image class |
|
1694 | 1694 | * :ghissue:`2372`: interactiveshell.py misses urllib and io_open imports |
|
1695 | 1695 | * :ghissue:`2371`: iPython not working |
|
1696 | 1696 | * :ghissue:`2367`: Tab expansion moves to next cell in notebook |
|
1697 | 1697 | * :ghissue:`2359`: nbviever alters the order of print and display() output |
|
1698 | 1698 | * :ghissue:`2227`: print name for IPython Notebooks has become uninformative |
|
1699 | 1699 | * :ghissue:`2361`: client doesn't use connection file's 'location' in disambiguating 'interface' |
|
1700 | 1700 | * :ghissue:`2357`: failing traceback in terminal ipython for first exception |
|
1701 | 1701 | * :ghissue:`2343`: Installing in a python 3.3b2 or python 3.3rc1 virtual environment. |
|
1702 | 1702 | * :ghissue:`2315`: Failure in test: "Test we're not loading modules on startup that we shouldn't." |
|
1703 | 1703 | * :ghissue:`2351`: Multiple Notebook Apps: cookies not port specific, clash with each other |
|
1704 | 1704 | * :ghissue:`2350`: running unittest from qtconsole prints output to terminal |
|
1705 | 1705 | * :ghissue:`2303`: remote tracebacks broken since 952d0d6 (PR #2223) |
|
1706 | 1706 | * :ghissue:`2330`: qtconsole does not hightlight tab-completion suggestion with custom stylesheet |
|
1707 | 1707 | * :ghissue:`2325`: Parsing Tex formula fails in Notebook |
|
1708 | 1708 | * :ghissue:`2324`: Parsing Tex formula fails |
|
1709 | 1709 | * :ghissue:`1474`: Add argument to `run -n` for custom namespace |
|
1710 | 1710 | * :ghissue:`2318`: C-m n/p don't work in Markdown cells in the notebook |
|
1711 | 1711 | * :ghissue:`2309`: time.time() in ipython notebook producing impossible results |
|
1712 | 1712 | * :ghissue:`2307`: schedule tasks on newly arrived engines |
|
1713 | 1713 | * :ghissue:`2313`: Allow Notebook HTML/JS to send messages to Python code |
|
1714 | 1714 | * :ghissue:`2304`: ipengine throws KeyError: url |
|
1715 | 1715 | * :ghissue:`1878`: shell access using ! will not fill class or function scope vars |
|
1716 | 1716 | * :ghissue:`2253`: %paste does not retrieve clipboard contents under screen/tmux on OS X |
|
1717 | 1717 | * :ghissue:`1510`: Add-on (or Monkey-patch) infrastructure for HTML notebook |
|
1718 | 1718 | * :ghissue:`2273`: triple quote and %s at beginning of line with %paste |
|
1719 | 1719 | * :ghissue:`2243`: Regression in .embed() |
|
1720 | 1720 | * :ghissue:`2266`: SSH passwordless check with OpenSSH checks for the wrong thing |
|
1721 | 1721 | * :ghissue:`2217`: Change NewNotebook handler to use 30x redirect |
|
1722 | 1722 | * :ghissue:`2276`: config option for disabling history store |
|
1723 | 1723 | * :ghissue:`2239`: can't use parallel.Reference in view.map |
|
1724 | 1724 | * :ghissue:`2272`: Sympy piecewise messed up rendering |
|
1725 | 1725 | * :ghissue:`2252`: %paste throws an exception with empty clipboard |
|
1726 | 1726 | * :ghissue:`2259`: git-mpr is currently broken |
|
1727 | 1727 | * :ghissue:`2247`: Variable expansion in shell commands should work in substrings |
|
1728 | 1728 | * :ghissue:`2026`: Run 'fast' tests only |
|
1729 | 1729 | * :ghissue:`2241`: read a list of notebooks on server and bring into browser only notebook |
|
1730 | 1730 | * :ghissue:`2237`: please put python and text editor in the web only ipython |
|
1731 | 1731 | * :ghissue:`2053`: Improvements to the IPython.display.Image object |
|
1732 | 1732 | * :ghissue:`1456`: ERROR: Internal Python error in the inspect module. |
|
1733 | 1733 | * :ghissue:`2221`: Avoid importing from IPython.parallel in core |
|
1734 | 1734 | * :ghissue:`2213`: Can't trigger startup code in Engines |
|
1735 | 1735 | * :ghissue:`1464`: Strange behavior for backspace with lines ending with more than 4 spaces in notebook |
|
1736 | 1736 | * :ghissue:`2187`: NaN in object_info_reply JSON causes parse error |
|
1737 | 1737 | * :ghissue:`214`: system command requiring administrative privileges |
|
1738 | 1738 | * :ghissue:`2195`: Unknown option `no-edit` in git-mpr |
|
1739 | 1739 | * :ghissue:`2201`: Add documentation build to tools/test_pr.py |
|
1740 | 1740 | * :ghissue:`2205`: Command-line option for default Notebook output collapsing behavior |
|
1741 | 1741 | * :ghissue:`1927`: toggle between inline and floating figures |
|
1742 | 1742 | * :ghissue:`2171`: Can't start StarCluster after upgrading to IPython 0.13 |
|
1743 | 1743 | * :ghissue:`2173`: oct2py v >= 0.3.1 doesn't need h5py anymore |
|
1744 | 1744 | * :ghissue:`2099`: storemagic needs to use self.shell |
|
1745 | 1745 | * :ghissue:`2166`: DirectView map_sync() with Lambdas Using Generators |
|
1746 | 1746 | * :ghissue:`2091`: Unable to use print_stats after %prun -r in notebook |
|
1747 | 1747 | * :ghissue:`2132`: Add fail-over for pastebin |
|
1748 | 1748 | * :ghissue:`2156`: Make it possible to install ipython without nasty gui dependencies |
|
1749 | 1749 | * :ghissue:`2154`: Scrolled long output should be off in print view by default |
|
1750 | 1750 | * :ghissue:`2162`: Tab completion does not work with IPython.embed_kernel() |
|
1751 | 1751 | * :ghissue:`2157`: iPython 0.13 / github-master cannot create logfile from scratch |
|
1752 | 1752 | * :ghissue:`2151`: missing newline when a magic is called from the qtconsole menu |
|
1753 | 1753 | * :ghissue:`2139`: 00_notebook_tour Image example broken on master |
|
1754 | 1754 | * :ghissue:`2143`: Add a %%cython_annotate magic |
|
1755 | 1755 | * :ghissue:`2135`: Running IPython from terminal |
|
1756 | 1756 | * :ghissue:`2093`: Makefile for building Sphinx documentation on Windows |
|
1757 | 1757 | * :ghissue:`2122`: Bug in pretty printing |
|
1758 | 1758 | * :ghissue:`2120`: Notebook "Make a Copy..." keeps opening duplicates in the same tab |
|
1759 | 1759 | * :ghissue:`1997`: password cannot be used with url prefix |
|
1760 | 1760 | * :ghissue:`2129`: help/doc displayed multiple times if requested in loop |
|
1761 | 1761 | * :ghissue:`2121`: ipdb does not support input history in qtconsole |
|
1762 | 1762 | * :ghissue:`2114`: %logstart doesn't log |
|
1763 | 1763 | * :ghissue:`2085`: %ed magic fails in qtconsole |
|
1764 | 1764 | * :ghissue:`2119`: iPython fails to run on MacOS Lion |
|
1765 | 1765 | * :ghissue:`2052`: %pylab inline magic does not work on windows |
|
1766 | 1766 | * :ghissue:`2111`: Ipython won't start on W7 |
|
1767 | 1767 | * :ghissue:`2112`: Strange internal traceback |
|
1768 | 1768 | * :ghissue:`2108`: Backslash (\) at the end of the line behavior different from default Python |
|
1769 | 1769 | * :ghissue:`1425`: Ampersands can't be typed sometimes in notebook cells |
|
1770 | 1770 | * :ghissue:`1513`: Add expand/collapse support for long output elements like stdout and tracebacks |
|
1771 | 1771 | * :ghissue:`2087`: error when starting ipython |
|
1772 | 1772 | * :ghissue:`2103`: Ability to run notebook file from commandline |
|
1773 | 1773 | * :ghissue:`2082`: Qt Console output spacing |
|
1774 | 1774 | * :ghissue:`2083`: Test failures with Python 3.2 and PYTHONWARNINGS="d" |
|
1775 | 1775 | * :ghissue:`2094`: about inline |
|
1776 | 1776 | * :ghissue:`2077`: Starting IPython3 on the terminal |
|
1777 | 1777 | * :ghissue:`1760`: easy_install ipython fails on py3.2-win32 |
|
1778 | 1778 | * :ghissue:`2075`: Local Mathjax install causes iptest3 error under python3 |
|
1779 | 1779 | * :ghissue:`2057`: setup fails for python3 with LANG=C |
|
1780 | 1780 | * :ghissue:`2070`: shebang on Windows |
|
1781 | 1781 | * :ghissue:`2054`: sys_info missing git hash in sdists |
|
1782 | 1782 | * :ghissue:`2059`: duplicate and modified files in documentation |
|
1783 | 1783 | * :ghissue:`2056`: except-shadows-builtin osm.py:687 |
|
1784 | 1784 | * :ghissue:`2058`: hyphen-used-as-minus-sign in manpages |
General Comments 0
You need to be logged in to leave comments.
Login now