Show More
|
1 | NO CONTENT: new file 100644, binary diff hidden |
@@ -1,65 +1,57 b'' | |||
|
1 | 1 | # encoding: utf-8 |
|
2 | 2 | """ |
|
3 | 3 | IPython: tools for interactive and parallel computing in Python. |
|
4 | 4 | |
|
5 | 5 | http://ipython.org |
|
6 | 6 | """ |
|
7 | 7 | #----------------------------------------------------------------------------- |
|
8 | 8 | # Copyright (c) 2008-2011, IPython Development Team. |
|
9 | 9 | # Copyright (c) 2001-2007, Fernando Perez <fernando.perez@colorado.edu> |
|
10 | 10 | # Copyright (c) 2001, Janko Hauser <jhauser@zscout.de> |
|
11 | 11 | # Copyright (c) 2001, Nathaniel Gray <n8gray@caltech.edu> |
|
12 | 12 | # |
|
13 | 13 | # Distributed under the terms of the Modified BSD License. |
|
14 | 14 | # |
|
15 | 15 | # The full license is in the file COPYING.txt, distributed with this software. |
|
16 | 16 | #----------------------------------------------------------------------------- |
|
17 | 17 | |
|
18 | 18 | #----------------------------------------------------------------------------- |
|
19 | 19 | # Imports |
|
20 | 20 | #----------------------------------------------------------------------------- |
|
21 | 21 | from __future__ import absolute_import |
|
22 | 22 | |
|
23 | 23 | import os |
|
24 | 24 | import sys |
|
25 | 25 | |
|
26 | 26 | #----------------------------------------------------------------------------- |
|
27 | 27 | # Setup everything |
|
28 | 28 | #----------------------------------------------------------------------------- |
|
29 | 29 | |
|
30 | 30 | # Don't forget to also update setup.py when this changes! |
|
31 | 31 | if sys.version[0:3] < '2.6': |
|
32 | 32 | raise ImportError('Python Version 2.6 or above is required for IPython.') |
|
33 | 33 | |
|
34 | 34 | # Make it easy to import extensions - they are always directly on pythonpath. |
|
35 | 35 | # Therefore, non-IPython modules can be added to extensions directory. |
|
36 | 36 | # This should probably be in ipapp.py. |
|
37 | 37 | sys.path.append(os.path.join(os.path.dirname(__file__), "extensions")) |
|
38 | 38 | |
|
39 | 39 | #----------------------------------------------------------------------------- |
|
40 | 40 | # Setup the top level names |
|
41 | 41 | #----------------------------------------------------------------------------- |
|
42 | 42 | |
|
43 | 43 | from .config.loader import Config |
|
44 | 44 | from .core import release |
|
45 | 45 | from .core.application import Application |
|
46 | # Todo: Should these be imported here? We need to rethink what is imported in | |
|
47 | # this module. | |
|
48 | #from .core.display import ( | |
|
49 | # display, display_pretty, display_html, display_latex, | |
|
50 | # display_png, display_jpeg, display_svg, display_json, | |
|
51 | # display_javascript, HTML, SVG, Math, Image, JSON, | |
|
52 | # Javascript, Pretty | |
|
53 | #) | |
|
54 | 46 | from .frontend.terminal.embed import embed |
|
55 | 47 | from .core.error import TryNext |
|
56 | 48 | from .core.interactiveshell import InteractiveShell |
|
57 | 49 | from .testing import test |
|
58 | 50 | from .utils.sysinfo import sys_info |
|
59 | 51 | |
|
60 | 52 | # Release data |
|
61 | 53 | __author__ = '' |
|
62 | 54 | for author, email in release.authors.itervalues(): |
|
63 | 55 | __author__ += author + ' <' + email + '>\n' |
|
64 | 56 | __license__ = release.license |
|
65 | 57 | __version__ = release.version |
@@ -1,3563 +1,3562 b'' | |||
|
1 | 1 | # encoding: utf-8 |
|
2 | 2 | """Magic functions for InteractiveShell. |
|
3 | 3 | """ |
|
4 | 4 | |
|
5 | 5 | #----------------------------------------------------------------------------- |
|
6 | 6 | # Copyright (C) 2001 Janko Hauser <jhauser@zscout.de> and |
|
7 | 7 | # Copyright (C) 2001-2007 Fernando Perez <fperez@colorado.edu> |
|
8 | 8 | # Copyright (C) 2008-2009 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 | import __builtin__ |
|
19 | 19 | import __future__ |
|
20 | 20 | import bdb |
|
21 | 21 | import inspect |
|
22 | 22 | import os |
|
23 | 23 | import sys |
|
24 | 24 | import shutil |
|
25 | 25 | import re |
|
26 | 26 | import time |
|
27 | 27 | import textwrap |
|
28 | 28 | from cStringIO import StringIO |
|
29 | 29 | from getopt import getopt,GetoptError |
|
30 | 30 | from pprint import pformat |
|
31 | 31 | from xmlrpclib import ServerProxy |
|
32 | 32 | |
|
33 | 33 | # cProfile was added in Python2.5 |
|
34 | 34 | try: |
|
35 | 35 | import cProfile as profile |
|
36 | 36 | import pstats |
|
37 | 37 | except ImportError: |
|
38 | 38 | # profile isn't bundled by default in Debian for license reasons |
|
39 | 39 | try: |
|
40 | 40 | import profile,pstats |
|
41 | 41 | except ImportError: |
|
42 | 42 | profile = pstats = None |
|
43 | 43 | |
|
44 | 44 | import IPython |
|
45 | 45 | from IPython.core import debugger, oinspect |
|
46 | 46 | from IPython.core.error import TryNext |
|
47 | 47 | from IPython.core.error import UsageError |
|
48 | 48 | from IPython.core.fakemodule import FakeModule |
|
49 | 49 | from IPython.core.profiledir import ProfileDir |
|
50 | 50 | from IPython.core.macro import Macro |
|
51 | from IPython.core import magic_arguments | |
|
52 | from IPython.core import page | |
|
51 | from IPython.core import magic_arguments, page | |
|
53 | 52 | from IPython.core.prefilter import ESC_MAGIC |
|
54 | 53 | from IPython.lib.pylabtools import mpl_runner |
|
55 | 54 | from IPython.testing.skipdoctest import skip_doctest |
|
56 | 55 | from IPython.utils.io import file_read, nlprint |
|
57 | 56 | from IPython.utils.path import get_py_filename |
|
58 | 57 | from IPython.utils.process import arg_split, abbrev_cwd |
|
59 | 58 | from IPython.utils.terminal import set_term_title |
|
60 | 59 | from IPython.utils.text import LSString, SList, format_screen |
|
61 | 60 | from IPython.utils.timing import clock, clock2 |
|
62 | 61 | from IPython.utils.warn import warn, error |
|
63 | 62 | from IPython.utils.ipstruct import Struct |
|
64 | 63 | import IPython.utils.generics |
|
65 | 64 | |
|
66 | 65 | #----------------------------------------------------------------------------- |
|
67 | 66 | # Utility functions |
|
68 | 67 | #----------------------------------------------------------------------------- |
|
69 | 68 | |
|
70 | 69 | def on_off(tag): |
|
71 | 70 | """Return an ON/OFF string for a 1/0 input. Simple utility function.""" |
|
72 | 71 | return ['OFF','ON'][tag] |
|
73 | 72 | |
|
74 | 73 | class Bunch: pass |
|
75 | 74 | |
|
76 | 75 | def compress_dhist(dh): |
|
77 | 76 | head, tail = dh[:-10], dh[-10:] |
|
78 | 77 | |
|
79 | 78 | newhead = [] |
|
80 | 79 | done = set() |
|
81 | 80 | for h in head: |
|
82 | 81 | if h in done: |
|
83 | 82 | continue |
|
84 | 83 | newhead.append(h) |
|
85 | 84 | done.add(h) |
|
86 | 85 | |
|
87 | 86 | return newhead + tail |
|
88 | 87 | |
|
89 | 88 | def needs_local_scope(func): |
|
90 | 89 | """Decorator to mark magic functions which need to local scope to run.""" |
|
91 | 90 | func.needs_local_scope = True |
|
92 | 91 | return func |
|
93 | 92 | |
|
94 | 93 | # Used for exception handling in magic_edit |
|
95 | 94 | class MacroToEdit(ValueError): pass |
|
96 | 95 | |
|
97 | 96 | #*************************************************************************** |
|
98 | 97 | # Main class implementing Magic functionality |
|
99 | 98 | |
|
100 | 99 | # XXX - for some odd reason, if Magic is made a new-style class, we get errors |
|
101 | 100 | # on construction of the main InteractiveShell object. Something odd is going |
|
102 | 101 | # on with super() calls, Configurable and the MRO... For now leave it as-is, but |
|
103 | 102 | # eventually this needs to be clarified. |
|
104 | 103 | # BG: This is because InteractiveShell inherits from this, but is itself a |
|
105 | 104 | # Configurable. This messes up the MRO in some way. The fix is that we need to |
|
106 | 105 | # make Magic a configurable that InteractiveShell does not subclass. |
|
107 | 106 | |
|
108 | 107 | class Magic: |
|
109 | 108 | """Magic functions for InteractiveShell. |
|
110 | 109 | |
|
111 | 110 | Shell functions which can be reached as %function_name. All magic |
|
112 | 111 | functions should accept a string, which they can parse for their own |
|
113 | 112 | needs. This can make some functions easier to type, eg `%cd ../` |
|
114 | 113 | vs. `%cd("../")` |
|
115 | 114 | |
|
116 | 115 | ALL definitions MUST begin with the prefix magic_. The user won't need it |
|
117 | 116 | at the command line, but it is is needed in the definition. """ |
|
118 | 117 | |
|
119 | 118 | # class globals |
|
120 | 119 | auto_status = ['Automagic is OFF, % prefix IS needed for magic functions.', |
|
121 | 120 | 'Automagic is ON, % prefix NOT needed for magic functions.'] |
|
122 | 121 | |
|
123 | 122 | #...................................................................... |
|
124 | 123 | # some utility functions |
|
125 | 124 | |
|
126 | 125 | def __init__(self,shell): |
|
127 | 126 | |
|
128 | 127 | self.options_table = {} |
|
129 | 128 | if profile is None: |
|
130 | 129 | self.magic_prun = self.profile_missing_notice |
|
131 | 130 | self.shell = shell |
|
132 | 131 | |
|
133 | 132 | # namespace for holding state we may need |
|
134 | 133 | self._magic_state = Bunch() |
|
135 | 134 | |
|
136 | 135 | def profile_missing_notice(self, *args, **kwargs): |
|
137 | 136 | error("""\ |
|
138 | 137 | The profile module could not be found. It has been removed from the standard |
|
139 | 138 | python packages because of its non-free license. To use profiling, install the |
|
140 | 139 | python-profiler package from non-free.""") |
|
141 | 140 | |
|
142 | 141 | def default_option(self,fn,optstr): |
|
143 | 142 | """Make an entry in the options_table for fn, with value optstr""" |
|
144 | 143 | |
|
145 | 144 | if fn not in self.lsmagic(): |
|
146 | 145 | error("%s is not a magic function" % fn) |
|
147 | 146 | self.options_table[fn] = optstr |
|
148 | 147 | |
|
149 | 148 | def lsmagic(self): |
|
150 | 149 | """Return a list of currently available magic functions. |
|
151 | 150 | |
|
152 | 151 | Gives a list of the bare names after mangling (['ls','cd', ...], not |
|
153 | 152 | ['magic_ls','magic_cd',...]""" |
|
154 | 153 | |
|
155 | 154 | # FIXME. This needs a cleanup, in the way the magics list is built. |
|
156 | 155 | |
|
157 | 156 | # magics in class definition |
|
158 | 157 | class_magic = lambda fn: fn.startswith('magic_') and \ |
|
159 | 158 | callable(Magic.__dict__[fn]) |
|
160 | 159 | # in instance namespace (run-time user additions) |
|
161 | 160 | inst_magic = lambda fn: fn.startswith('magic_') and \ |
|
162 | 161 | callable(self.__dict__[fn]) |
|
163 | 162 | # and bound magics by user (so they can access self): |
|
164 | 163 | inst_bound_magic = lambda fn: fn.startswith('magic_') and \ |
|
165 | 164 | callable(self.__class__.__dict__[fn]) |
|
166 | 165 | magics = filter(class_magic,Magic.__dict__.keys()) + \ |
|
167 | 166 | filter(inst_magic,self.__dict__.keys()) + \ |
|
168 | 167 | filter(inst_bound_magic,self.__class__.__dict__.keys()) |
|
169 | 168 | out = [] |
|
170 | 169 | for fn in set(magics): |
|
171 | 170 | out.append(fn.replace('magic_','',1)) |
|
172 | 171 | out.sort() |
|
173 | 172 | return out |
|
174 | 173 | |
|
175 | 174 | def extract_input_lines(self, range_str, raw=False): |
|
176 | 175 | """Return as a string a set of input history slices. |
|
177 | 176 | |
|
178 | 177 | Inputs: |
|
179 | 178 | |
|
180 | 179 | - range_str: the set of slices is given as a string, like |
|
181 | 180 | "~5/6-~4/2 4:8 9", since this function is for use by magic functions |
|
182 | 181 | which get their arguments as strings. The number before the / is the |
|
183 | 182 | session number: ~n goes n back from the current session. |
|
184 | 183 | |
|
185 | 184 | Optional inputs: |
|
186 | 185 | |
|
187 | 186 | - raw(False): by default, the processed input is used. If this is |
|
188 | 187 | true, the raw input history is used instead. |
|
189 | 188 | |
|
190 | 189 | Note that slices can be called with two notations: |
|
191 | 190 | |
|
192 | 191 | N:M -> standard python form, means including items N...(M-1). |
|
193 | 192 | |
|
194 | 193 | N-M -> include items N..M (closed endpoint).""" |
|
195 | 194 | lines = self.shell.history_manager.\ |
|
196 | 195 | get_range_by_str(range_str, raw=raw) |
|
197 | 196 | return "\n".join(x for _, _, x in lines) |
|
198 | 197 | |
|
199 | 198 | def arg_err(self,func): |
|
200 | 199 | """Print docstring if incorrect arguments were passed""" |
|
201 | 200 | print 'Error in arguments:' |
|
202 | 201 | print oinspect.getdoc(func) |
|
203 | 202 | |
|
204 | 203 | def format_latex(self,strng): |
|
205 | 204 | """Format a string for latex inclusion.""" |
|
206 | 205 | |
|
207 | 206 | # Characters that need to be escaped for latex: |
|
208 | 207 | escape_re = re.compile(r'(%|_|\$|#|&)',re.MULTILINE) |
|
209 | 208 | # Magic command names as headers: |
|
210 | 209 | cmd_name_re = re.compile(r'^(%s.*?):' % ESC_MAGIC, |
|
211 | 210 | re.MULTILINE) |
|
212 | 211 | # Magic commands |
|
213 | 212 | cmd_re = re.compile(r'(?P<cmd>%s.+?\b)(?!\}\}:)' % ESC_MAGIC, |
|
214 | 213 | re.MULTILINE) |
|
215 | 214 | # Paragraph continue |
|
216 | 215 | par_re = re.compile(r'\\$',re.MULTILINE) |
|
217 | 216 | |
|
218 | 217 | # The "\n" symbol |
|
219 | 218 | newline_re = re.compile(r'\\n') |
|
220 | 219 | |
|
221 | 220 | # Now build the string for output: |
|
222 | 221 | #strng = cmd_name_re.sub(r'\n\\texttt{\\textsl{\\large \1}}:',strng) |
|
223 | 222 | strng = cmd_name_re.sub(r'\n\\bigskip\n\\texttt{\\textbf{ \1}}:', |
|
224 | 223 | strng) |
|
225 | 224 | strng = cmd_re.sub(r'\\texttt{\g<cmd>}',strng) |
|
226 | 225 | strng = par_re.sub(r'\\\\',strng) |
|
227 | 226 | strng = escape_re.sub(r'\\\1',strng) |
|
228 | 227 | strng = newline_re.sub(r'\\textbackslash{}n',strng) |
|
229 | 228 | return strng |
|
230 | 229 | |
|
231 | 230 | def parse_options(self,arg_str,opt_str,*long_opts,**kw): |
|
232 | 231 | """Parse options passed to an argument string. |
|
233 | 232 | |
|
234 | 233 | The interface is similar to that of getopt(), but it returns back a |
|
235 | 234 | Struct with the options as keys and the stripped argument string still |
|
236 | 235 | as a string. |
|
237 | 236 | |
|
238 | 237 | arg_str is quoted as a true sys.argv vector by using shlex.split. |
|
239 | 238 | This allows us to easily expand variables, glob files, quote |
|
240 | 239 | arguments, etc. |
|
241 | 240 | |
|
242 | 241 | Options: |
|
243 | 242 | -mode: default 'string'. If given as 'list', the argument string is |
|
244 | 243 | returned as a list (split on whitespace) instead of a string. |
|
245 | 244 | |
|
246 | 245 | -list_all: put all option values in lists. Normally only options |
|
247 | 246 | appearing more than once are put in a list. |
|
248 | 247 | |
|
249 | 248 | -posix (True): whether to split the input line in POSIX mode or not, |
|
250 | 249 | as per the conventions outlined in the shlex module from the |
|
251 | 250 | standard library.""" |
|
252 | 251 | |
|
253 | 252 | # inject default options at the beginning of the input line |
|
254 | 253 | caller = sys._getframe(1).f_code.co_name.replace('magic_','') |
|
255 | 254 | arg_str = '%s %s' % (self.options_table.get(caller,''),arg_str) |
|
256 | 255 | |
|
257 | 256 | mode = kw.get('mode','string') |
|
258 | 257 | if mode not in ['string','list']: |
|
259 | 258 | raise ValueError,'incorrect mode given: %s' % mode |
|
260 | 259 | # Get options |
|
261 | 260 | list_all = kw.get('list_all',0) |
|
262 | 261 | posix = kw.get('posix', os.name == 'posix') |
|
263 | 262 | |
|
264 | 263 | # Check if we have more than one argument to warrant extra processing: |
|
265 | 264 | odict = {} # Dictionary with options |
|
266 | 265 | args = arg_str.split() |
|
267 | 266 | if len(args) >= 1: |
|
268 | 267 | # If the list of inputs only has 0 or 1 thing in it, there's no |
|
269 | 268 | # need to look for options |
|
270 | 269 | argv = arg_split(arg_str,posix) |
|
271 | 270 | # Do regular option processing |
|
272 | 271 | try: |
|
273 | 272 | opts,args = getopt(argv,opt_str,*long_opts) |
|
274 | 273 | except GetoptError,e: |
|
275 | 274 | raise UsageError('%s ( allowed: "%s" %s)' % (e.msg,opt_str, |
|
276 | 275 | " ".join(long_opts))) |
|
277 | 276 | for o,a in opts: |
|
278 | 277 | if o.startswith('--'): |
|
279 | 278 | o = o[2:] |
|
280 | 279 | else: |
|
281 | 280 | o = o[1:] |
|
282 | 281 | try: |
|
283 | 282 | odict[o].append(a) |
|
284 | 283 | except AttributeError: |
|
285 | 284 | odict[o] = [odict[o],a] |
|
286 | 285 | except KeyError: |
|
287 | 286 | if list_all: |
|
288 | 287 | odict[o] = [a] |
|
289 | 288 | else: |
|
290 | 289 | odict[o] = a |
|
291 | 290 | |
|
292 | 291 | # Prepare opts,args for return |
|
293 | 292 | opts = Struct(odict) |
|
294 | 293 | if mode == 'string': |
|
295 | 294 | args = ' '.join(args) |
|
296 | 295 | |
|
297 | 296 | return opts,args |
|
298 | 297 | |
|
299 | 298 | #...................................................................... |
|
300 | 299 | # And now the actual magic functions |
|
301 | 300 | |
|
302 | 301 | # Functions for IPython shell work (vars,funcs, config, etc) |
|
303 | 302 | def magic_lsmagic(self, parameter_s = ''): |
|
304 | 303 | """List currently available magic functions.""" |
|
305 | 304 | mesc = ESC_MAGIC |
|
306 | 305 | print 'Available magic functions:\n'+mesc+\ |
|
307 | 306 | (' '+mesc).join(self.lsmagic()) |
|
308 | 307 | print '\n' + Magic.auto_status[self.shell.automagic] |
|
309 | 308 | return None |
|
310 | 309 | |
|
311 | 310 | def magic_magic(self, parameter_s = ''): |
|
312 | 311 | """Print information about the magic function system. |
|
313 | 312 | |
|
314 | 313 | Supported formats: -latex, -brief, -rest |
|
315 | 314 | """ |
|
316 | 315 | |
|
317 | 316 | mode = '' |
|
318 | 317 | try: |
|
319 | 318 | if parameter_s.split()[0] == '-latex': |
|
320 | 319 | mode = 'latex' |
|
321 | 320 | if parameter_s.split()[0] == '-brief': |
|
322 | 321 | mode = 'brief' |
|
323 | 322 | if parameter_s.split()[0] == '-rest': |
|
324 | 323 | mode = 'rest' |
|
325 | 324 | rest_docs = [] |
|
326 | 325 | except: |
|
327 | 326 | pass |
|
328 | 327 | |
|
329 | 328 | magic_docs = [] |
|
330 | 329 | for fname in self.lsmagic(): |
|
331 | 330 | mname = 'magic_' + fname |
|
332 | 331 | for space in (Magic,self,self.__class__): |
|
333 | 332 | try: |
|
334 | 333 | fn = space.__dict__[mname] |
|
335 | 334 | except KeyError: |
|
336 | 335 | pass |
|
337 | 336 | else: |
|
338 | 337 | break |
|
339 | 338 | if mode == 'brief': |
|
340 | 339 | # only first line |
|
341 | 340 | if fn.__doc__: |
|
342 | 341 | fndoc = fn.__doc__.split('\n',1)[0] |
|
343 | 342 | else: |
|
344 | 343 | fndoc = 'No documentation' |
|
345 | 344 | else: |
|
346 | 345 | if fn.__doc__: |
|
347 | 346 | fndoc = fn.__doc__.rstrip() |
|
348 | 347 | else: |
|
349 | 348 | fndoc = 'No documentation' |
|
350 | 349 | |
|
351 | 350 | |
|
352 | 351 | if mode == 'rest': |
|
353 | 352 | rest_docs.append('**%s%s**::\n\n\t%s\n\n' %(ESC_MAGIC, |
|
354 | 353 | fname,fndoc)) |
|
355 | 354 | |
|
356 | 355 | else: |
|
357 | 356 | magic_docs.append('%s%s:\n\t%s\n' %(ESC_MAGIC, |
|
358 | 357 | fname,fndoc)) |
|
359 | 358 | |
|
360 | 359 | magic_docs = ''.join(magic_docs) |
|
361 | 360 | |
|
362 | 361 | if mode == 'rest': |
|
363 | 362 | return "".join(rest_docs) |
|
364 | 363 | |
|
365 | 364 | if mode == 'latex': |
|
366 | 365 | print self.format_latex(magic_docs) |
|
367 | 366 | return |
|
368 | 367 | else: |
|
369 | 368 | magic_docs = format_screen(magic_docs) |
|
370 | 369 | if mode == 'brief': |
|
371 | 370 | return magic_docs |
|
372 | 371 | |
|
373 | 372 | outmsg = """ |
|
374 | 373 | IPython's 'magic' functions |
|
375 | 374 | =========================== |
|
376 | 375 | |
|
377 | 376 | The magic function system provides a series of functions which allow you to |
|
378 | 377 | control the behavior of IPython itself, plus a lot of system-type |
|
379 | 378 | features. All these functions are prefixed with a % character, but parameters |
|
380 | 379 | are given without parentheses or quotes. |
|
381 | 380 | |
|
382 | 381 | NOTE: If you have 'automagic' enabled (via the command line option or with the |
|
383 | 382 | %automagic function), you don't need to type in the % explicitly. By default, |
|
384 | 383 | IPython ships with automagic on, so you should only rarely need the % escape. |
|
385 | 384 | |
|
386 | 385 | Example: typing '%cd mydir' (without the quotes) changes you working directory |
|
387 | 386 | to 'mydir', if it exists. |
|
388 | 387 | |
|
389 | 388 | For a list of the available magic functions, use %lsmagic. For a description |
|
390 | 389 | of any of them, type %magic_name?, e.g. '%cd?'. |
|
391 | 390 | |
|
392 | 391 | Currently the magic system has the following functions:\n""" |
|
393 | 392 | |
|
394 | 393 | mesc = ESC_MAGIC |
|
395 | 394 | outmsg = ("%s\n%s\n\nSummary of magic functions (from %slsmagic):" |
|
396 | 395 | "\n\n%s%s\n\n%s" % (outmsg, |
|
397 | 396 | magic_docs,mesc,mesc, |
|
398 | 397 | (' '+mesc).join(self.lsmagic()), |
|
399 | 398 | Magic.auto_status[self.shell.automagic] ) ) |
|
400 | 399 | page.page(outmsg) |
|
401 | 400 | |
|
402 | 401 | def magic_automagic(self, parameter_s = ''): |
|
403 | 402 | """Make magic functions callable without having to type the initial %. |
|
404 | 403 | |
|
405 | 404 | Without argumentsl toggles on/off (when off, you must call it as |
|
406 | 405 | %automagic, of course). With arguments it sets the value, and you can |
|
407 | 406 | use any of (case insensitive): |
|
408 | 407 | |
|
409 | 408 | - on,1,True: to activate |
|
410 | 409 | |
|
411 | 410 | - off,0,False: to deactivate. |
|
412 | 411 | |
|
413 | 412 | Note that magic functions have lowest priority, so if there's a |
|
414 | 413 | variable whose name collides with that of a magic fn, automagic won't |
|
415 | 414 | work for that function (you get the variable instead). However, if you |
|
416 | 415 | delete the variable (del var), the previously shadowed magic function |
|
417 | 416 | becomes visible to automagic again.""" |
|
418 | 417 | |
|
419 | 418 | arg = parameter_s.lower() |
|
420 | 419 | if parameter_s in ('on','1','true'): |
|
421 | 420 | self.shell.automagic = True |
|
422 | 421 | elif parameter_s in ('off','0','false'): |
|
423 | 422 | self.shell.automagic = False |
|
424 | 423 | else: |
|
425 | 424 | self.shell.automagic = not self.shell.automagic |
|
426 | 425 | print '\n' + Magic.auto_status[self.shell.automagic] |
|
427 | 426 | |
|
428 | 427 | @skip_doctest |
|
429 | 428 | def magic_autocall(self, parameter_s = ''): |
|
430 | 429 | """Make functions callable without having to type parentheses. |
|
431 | 430 | |
|
432 | 431 | Usage: |
|
433 | 432 | |
|
434 | 433 | %autocall [mode] |
|
435 | 434 | |
|
436 | 435 | The mode can be one of: 0->Off, 1->Smart, 2->Full. If not given, the |
|
437 | 436 | value is toggled on and off (remembering the previous state). |
|
438 | 437 | |
|
439 | 438 | In more detail, these values mean: |
|
440 | 439 | |
|
441 | 440 | 0 -> fully disabled |
|
442 | 441 | |
|
443 | 442 | 1 -> active, but do not apply if there are no arguments on the line. |
|
444 | 443 | |
|
445 | 444 | In this mode, you get: |
|
446 | 445 | |
|
447 | 446 | In [1]: callable |
|
448 | 447 | Out[1]: <built-in function callable> |
|
449 | 448 | |
|
450 | 449 | In [2]: callable 'hello' |
|
451 | 450 | ------> callable('hello') |
|
452 | 451 | Out[2]: False |
|
453 | 452 | |
|
454 | 453 | 2 -> Active always. Even if no arguments are present, the callable |
|
455 | 454 | object is called: |
|
456 | 455 | |
|
457 | 456 | In [2]: float |
|
458 | 457 | ------> float() |
|
459 | 458 | Out[2]: 0.0 |
|
460 | 459 | |
|
461 | 460 | Note that even with autocall off, you can still use '/' at the start of |
|
462 | 461 | a line to treat the first argument on the command line as a function |
|
463 | 462 | and add parentheses to it: |
|
464 | 463 | |
|
465 | 464 | In [8]: /str 43 |
|
466 | 465 | ------> str(43) |
|
467 | 466 | Out[8]: '43' |
|
468 | 467 | |
|
469 | 468 | # all-random (note for auto-testing) |
|
470 | 469 | """ |
|
471 | 470 | |
|
472 | 471 | if parameter_s: |
|
473 | 472 | arg = int(parameter_s) |
|
474 | 473 | else: |
|
475 | 474 | arg = 'toggle' |
|
476 | 475 | |
|
477 | 476 | if not arg in (0,1,2,'toggle'): |
|
478 | 477 | error('Valid modes: (0->Off, 1->Smart, 2->Full') |
|
479 | 478 | return |
|
480 | 479 | |
|
481 | 480 | if arg in (0,1,2): |
|
482 | 481 | self.shell.autocall = arg |
|
483 | 482 | else: # toggle |
|
484 | 483 | if self.shell.autocall: |
|
485 | 484 | self._magic_state.autocall_save = self.shell.autocall |
|
486 | 485 | self.shell.autocall = 0 |
|
487 | 486 | else: |
|
488 | 487 | try: |
|
489 | 488 | self.shell.autocall = self._magic_state.autocall_save |
|
490 | 489 | except AttributeError: |
|
491 | 490 | self.shell.autocall = self._magic_state.autocall_save = 1 |
|
492 | 491 | |
|
493 | 492 | print "Automatic calling is:",['OFF','Smart','Full'][self.shell.autocall] |
|
494 | 493 | |
|
495 | 494 | |
|
496 | 495 | def magic_page(self, parameter_s=''): |
|
497 | 496 | """Pretty print the object and display it through a pager. |
|
498 | 497 | |
|
499 | 498 | %page [options] OBJECT |
|
500 | 499 | |
|
501 | 500 | If no object is given, use _ (last output). |
|
502 | 501 | |
|
503 | 502 | Options: |
|
504 | 503 | |
|
505 | 504 | -r: page str(object), don't pretty-print it.""" |
|
506 | 505 | |
|
507 | 506 | # After a function contributed by Olivier Aubert, slightly modified. |
|
508 | 507 | |
|
509 | 508 | # Process options/args |
|
510 | 509 | opts,args = self.parse_options(parameter_s,'r') |
|
511 | 510 | raw = 'r' in opts |
|
512 | 511 | |
|
513 | 512 | oname = args and args or '_' |
|
514 | 513 | info = self._ofind(oname) |
|
515 | 514 | if info['found']: |
|
516 | 515 | txt = (raw and str or pformat)( info['obj'] ) |
|
517 | 516 | page.page(txt) |
|
518 | 517 | else: |
|
519 | 518 | print 'Object `%s` not found' % oname |
|
520 | 519 | |
|
521 | 520 | def magic_profile(self, parameter_s=''): |
|
522 | 521 | """Print your currently active IPython profile.""" |
|
523 | 522 | print self.shell.profile |
|
524 | 523 | |
|
525 | 524 | def magic_pinfo(self, parameter_s='', namespaces=None): |
|
526 | 525 | """Provide detailed information about an object. |
|
527 | 526 | |
|
528 | 527 | '%pinfo object' is just a synonym for object? or ?object.""" |
|
529 | 528 | |
|
530 | 529 | #print 'pinfo par: <%s>' % parameter_s # dbg |
|
531 | 530 | |
|
532 | 531 | |
|
533 | 532 | # detail_level: 0 -> obj? , 1 -> obj?? |
|
534 | 533 | detail_level = 0 |
|
535 | 534 | # We need to detect if we got called as 'pinfo pinfo foo', which can |
|
536 | 535 | # happen if the user types 'pinfo foo?' at the cmd line. |
|
537 | 536 | pinfo,qmark1,oname,qmark2 = \ |
|
538 | 537 | re.match('(pinfo )?(\?*)(.*?)(\??$)',parameter_s).groups() |
|
539 | 538 | if pinfo or qmark1 or qmark2: |
|
540 | 539 | detail_level = 1 |
|
541 | 540 | if "*" in oname: |
|
542 | 541 | self.magic_psearch(oname) |
|
543 | 542 | else: |
|
544 | 543 | self.shell._inspect('pinfo', oname, detail_level=detail_level, |
|
545 | 544 | namespaces=namespaces) |
|
546 | 545 | |
|
547 | 546 | def magic_pinfo2(self, parameter_s='', namespaces=None): |
|
548 | 547 | """Provide extra detailed information about an object. |
|
549 | 548 | |
|
550 | 549 | '%pinfo2 object' is just a synonym for object?? or ??object.""" |
|
551 | 550 | self.shell._inspect('pinfo', parameter_s, detail_level=1, |
|
552 | 551 | namespaces=namespaces) |
|
553 | 552 | |
|
554 | 553 | @skip_doctest |
|
555 | 554 | def magic_pdef(self, parameter_s='', namespaces=None): |
|
556 | 555 | """Print the definition header for any callable object. |
|
557 | 556 | |
|
558 | 557 | If the object is a class, print the constructor information. |
|
559 | 558 | |
|
560 | 559 | Examples |
|
561 | 560 | -------- |
|
562 | 561 | :: |
|
563 | 562 | |
|
564 | 563 | In [3]: %pdef urllib.urlopen |
|
565 | 564 | urllib.urlopen(url, data=None, proxies=None) |
|
566 | 565 | """ |
|
567 | 566 | self._inspect('pdef',parameter_s, namespaces) |
|
568 | 567 | |
|
569 | 568 | def magic_pdoc(self, parameter_s='', namespaces=None): |
|
570 | 569 | """Print the docstring for an object. |
|
571 | 570 | |
|
572 | 571 | If the given object is a class, it will print both the class and the |
|
573 | 572 | constructor docstrings.""" |
|
574 | 573 | self._inspect('pdoc',parameter_s, namespaces) |
|
575 | 574 | |
|
576 | 575 | def magic_psource(self, parameter_s='', namespaces=None): |
|
577 | 576 | """Print (or run through pager) the source code for an object.""" |
|
578 | 577 | self._inspect('psource',parameter_s, namespaces) |
|
579 | 578 | |
|
580 | 579 | def magic_pfile(self, parameter_s=''): |
|
581 | 580 | """Print (or run through pager) the file where an object is defined. |
|
582 | 581 | |
|
583 | 582 | The file opens at the line where the object definition begins. IPython |
|
584 | 583 | will honor the environment variable PAGER if set, and otherwise will |
|
585 | 584 | do its best to print the file in a convenient form. |
|
586 | 585 | |
|
587 | 586 | If the given argument is not an object currently defined, IPython will |
|
588 | 587 | try to interpret it as a filename (automatically adding a .py extension |
|
589 | 588 | if needed). You can thus use %pfile as a syntax highlighting code |
|
590 | 589 | viewer.""" |
|
591 | 590 | |
|
592 | 591 | # first interpret argument as an object name |
|
593 | 592 | out = self._inspect('pfile',parameter_s) |
|
594 | 593 | # if not, try the input as a filename |
|
595 | 594 | if out == 'not found': |
|
596 | 595 | try: |
|
597 | 596 | filename = get_py_filename(parameter_s) |
|
598 | 597 | except IOError,msg: |
|
599 | 598 | print msg |
|
600 | 599 | return |
|
601 | 600 | page.page(self.shell.inspector.format(file(filename).read())) |
|
602 | 601 | |
|
603 | 602 | def magic_psearch(self, parameter_s=''): |
|
604 | 603 | """Search for object in namespaces by wildcard. |
|
605 | 604 | |
|
606 | 605 | %psearch [options] PATTERN [OBJECT TYPE] |
|
607 | 606 | |
|
608 | 607 | Note: ? can be used as a synonym for %psearch, at the beginning or at |
|
609 | 608 | the end: both a*? and ?a* are equivalent to '%psearch a*'. Still, the |
|
610 | 609 | rest of the command line must be unchanged (options come first), so |
|
611 | 610 | for example the following forms are equivalent |
|
612 | 611 | |
|
613 | 612 | %psearch -i a* function |
|
614 | 613 | -i a* function? |
|
615 | 614 | ?-i a* function |
|
616 | 615 | |
|
617 | 616 | Arguments: |
|
618 | 617 | |
|
619 | 618 | PATTERN |
|
620 | 619 | |
|
621 | 620 | where PATTERN is a string containing * as a wildcard similar to its |
|
622 | 621 | use in a shell. The pattern is matched in all namespaces on the |
|
623 | 622 | search path. By default objects starting with a single _ are not |
|
624 | 623 | matched, many IPython generated objects have a single |
|
625 | 624 | underscore. The default is case insensitive matching. Matching is |
|
626 | 625 | also done on the attributes of objects and not only on the objects |
|
627 | 626 | in a module. |
|
628 | 627 | |
|
629 | 628 | [OBJECT TYPE] |
|
630 | 629 | |
|
631 | 630 | Is the name of a python type from the types module. The name is |
|
632 | 631 | given in lowercase without the ending type, ex. StringType is |
|
633 | 632 | written string. By adding a type here only objects matching the |
|
634 | 633 | given type are matched. Using all here makes the pattern match all |
|
635 | 634 | types (this is the default). |
|
636 | 635 | |
|
637 | 636 | Options: |
|
638 | 637 | |
|
639 | 638 | -a: makes the pattern match even objects whose names start with a |
|
640 | 639 | single underscore. These names are normally ommitted from the |
|
641 | 640 | search. |
|
642 | 641 | |
|
643 | 642 | -i/-c: make the pattern case insensitive/sensitive. If neither of |
|
644 | 643 | these options is given, the default is read from your ipythonrc |
|
645 | 644 | file. The option name which sets this value is |
|
646 | 645 | 'wildcards_case_sensitive'. If this option is not specified in your |
|
647 | 646 | ipythonrc file, IPython's internal default is to do a case sensitive |
|
648 | 647 | search. |
|
649 | 648 | |
|
650 | 649 | -e/-s NAMESPACE: exclude/search a given namespace. The pattern you |
|
651 | 650 | specifiy can be searched in any of the following namespaces: |
|
652 | 651 | 'builtin', 'user', 'user_global','internal', 'alias', where |
|
653 | 652 | 'builtin' and 'user' are the search defaults. Note that you should |
|
654 | 653 | not use quotes when specifying namespaces. |
|
655 | 654 | |
|
656 | 655 | 'Builtin' contains the python module builtin, 'user' contains all |
|
657 | 656 | user data, 'alias' only contain the shell aliases and no python |
|
658 | 657 | objects, 'internal' contains objects used by IPython. The |
|
659 | 658 | 'user_global' namespace is only used by embedded IPython instances, |
|
660 | 659 | and it contains module-level globals. You can add namespaces to the |
|
661 | 660 | search with -s or exclude them with -e (these options can be given |
|
662 | 661 | more than once). |
|
663 | 662 | |
|
664 | 663 | Examples: |
|
665 | 664 | |
|
666 | 665 | %psearch a* -> objects beginning with an a |
|
667 | 666 | %psearch -e builtin a* -> objects NOT in the builtin space starting in a |
|
668 | 667 | %psearch a* function -> all functions beginning with an a |
|
669 | 668 | %psearch re.e* -> objects beginning with an e in module re |
|
670 | 669 | %psearch r*.e* -> objects that start with e in modules starting in r |
|
671 | 670 | %psearch r*.* string -> all strings in modules beginning with r |
|
672 | 671 | |
|
673 | 672 | Case sensitve search: |
|
674 | 673 | |
|
675 | 674 | %psearch -c a* list all object beginning with lower case a |
|
676 | 675 | |
|
677 | 676 | Show objects beginning with a single _: |
|
678 | 677 | |
|
679 | 678 | %psearch -a _* list objects beginning with a single underscore""" |
|
680 | 679 | try: |
|
681 | 680 | parameter_s = parameter_s.encode('ascii') |
|
682 | 681 | except UnicodeEncodeError: |
|
683 | 682 | print 'Python identifiers can only contain ascii characters.' |
|
684 | 683 | return |
|
685 | 684 | |
|
686 | 685 | # default namespaces to be searched |
|
687 | 686 | def_search = ['user','builtin'] |
|
688 | 687 | |
|
689 | 688 | # Process options/args |
|
690 | 689 | opts,args = self.parse_options(parameter_s,'cias:e:',list_all=True) |
|
691 | 690 | opt = opts.get |
|
692 | 691 | shell = self.shell |
|
693 | 692 | psearch = shell.inspector.psearch |
|
694 | 693 | |
|
695 | 694 | # select case options |
|
696 | 695 | if opts.has_key('i'): |
|
697 | 696 | ignore_case = True |
|
698 | 697 | elif opts.has_key('c'): |
|
699 | 698 | ignore_case = False |
|
700 | 699 | else: |
|
701 | 700 | ignore_case = not shell.wildcards_case_sensitive |
|
702 | 701 | |
|
703 | 702 | # Build list of namespaces to search from user options |
|
704 | 703 | def_search.extend(opt('s',[])) |
|
705 | 704 | ns_exclude = ns_exclude=opt('e',[]) |
|
706 | 705 | ns_search = [nm for nm in def_search if nm not in ns_exclude] |
|
707 | 706 | |
|
708 | 707 | # Call the actual search |
|
709 | 708 | try: |
|
710 | 709 | psearch(args,shell.ns_table,ns_search, |
|
711 | 710 | show_all=opt('a'),ignore_case=ignore_case) |
|
712 | 711 | except: |
|
713 | 712 | shell.showtraceback() |
|
714 | 713 | |
|
715 | 714 | @skip_doctest |
|
716 | 715 | def magic_who_ls(self, parameter_s=''): |
|
717 | 716 | """Return a sorted list of all interactive variables. |
|
718 | 717 | |
|
719 | 718 | If arguments are given, only variables of types matching these |
|
720 | 719 | arguments are returned. |
|
721 | 720 | |
|
722 | 721 | Examples |
|
723 | 722 | -------- |
|
724 | 723 | |
|
725 | 724 | Define two variables and list them with who_ls:: |
|
726 | 725 | |
|
727 | 726 | In [1]: alpha = 123 |
|
728 | 727 | |
|
729 | 728 | In [2]: beta = 'test' |
|
730 | 729 | |
|
731 | 730 | In [3]: %who_ls |
|
732 | 731 | Out[3]: ['alpha', 'beta'] |
|
733 | 732 | |
|
734 | 733 | In [4]: %who_ls int |
|
735 | 734 | Out[4]: ['alpha'] |
|
736 | 735 | |
|
737 | 736 | In [5]: %who_ls str |
|
738 | 737 | Out[5]: ['beta'] |
|
739 | 738 | """ |
|
740 | 739 | |
|
741 | 740 | user_ns = self.shell.user_ns |
|
742 | 741 | internal_ns = self.shell.internal_ns |
|
743 | 742 | user_ns_hidden = self.shell.user_ns_hidden |
|
744 | 743 | out = [ i for i in user_ns |
|
745 | 744 | if not i.startswith('_') \ |
|
746 | 745 | and not (i in internal_ns or i in user_ns_hidden) ] |
|
747 | 746 | |
|
748 | 747 | typelist = parameter_s.split() |
|
749 | 748 | if typelist: |
|
750 | 749 | typeset = set(typelist) |
|
751 | 750 | out = [i for i in out if type(user_ns[i]).__name__ in typeset] |
|
752 | 751 | |
|
753 | 752 | out.sort() |
|
754 | 753 | return out |
|
755 | 754 | |
|
756 | 755 | @skip_doctest |
|
757 | 756 | def magic_who(self, parameter_s=''): |
|
758 | 757 | """Print all interactive variables, with some minimal formatting. |
|
759 | 758 | |
|
760 | 759 | If any arguments are given, only variables whose type matches one of |
|
761 | 760 | these are printed. For example: |
|
762 | 761 | |
|
763 | 762 | %who function str |
|
764 | 763 | |
|
765 | 764 | will only list functions and strings, excluding all other types of |
|
766 | 765 | variables. To find the proper type names, simply use type(var) at a |
|
767 | 766 | command line to see how python prints type names. For example: |
|
768 | 767 | |
|
769 | 768 | In [1]: type('hello')\\ |
|
770 | 769 | Out[1]: <type 'str'> |
|
771 | 770 | |
|
772 | 771 | indicates that the type name for strings is 'str'. |
|
773 | 772 | |
|
774 | 773 | %who always excludes executed names loaded through your configuration |
|
775 | 774 | file and things which are internal to IPython. |
|
776 | 775 | |
|
777 | 776 | This is deliberate, as typically you may load many modules and the |
|
778 | 777 | purpose of %who is to show you only what you've manually defined. |
|
779 | 778 | |
|
780 | 779 | Examples |
|
781 | 780 | -------- |
|
782 | 781 | |
|
783 | 782 | Define two variables and list them with who:: |
|
784 | 783 | |
|
785 | 784 | In [1]: alpha = 123 |
|
786 | 785 | |
|
787 | 786 | In [2]: beta = 'test' |
|
788 | 787 | |
|
789 | 788 | In [3]: %who |
|
790 | 789 | alpha beta |
|
791 | 790 | |
|
792 | 791 | In [4]: %who int |
|
793 | 792 | alpha |
|
794 | 793 | |
|
795 | 794 | In [5]: %who str |
|
796 | 795 | beta |
|
797 | 796 | """ |
|
798 | 797 | |
|
799 | 798 | varlist = self.magic_who_ls(parameter_s) |
|
800 | 799 | if not varlist: |
|
801 | 800 | if parameter_s: |
|
802 | 801 | print 'No variables match your requested type.' |
|
803 | 802 | else: |
|
804 | 803 | print 'Interactive namespace is empty.' |
|
805 | 804 | return |
|
806 | 805 | |
|
807 | 806 | # if we have variables, move on... |
|
808 | 807 | count = 0 |
|
809 | 808 | for i in varlist: |
|
810 | 809 | print i+'\t', |
|
811 | 810 | count += 1 |
|
812 | 811 | if count > 8: |
|
813 | 812 | count = 0 |
|
814 | 813 | |
|
815 | 814 | |
|
816 | 815 | |
|
817 | 816 | @skip_doctest |
|
818 | 817 | def magic_whos(self, parameter_s=''): |
|
819 | 818 | """Like %who, but gives some extra information about each variable. |
|
820 | 819 | |
|
821 | 820 | The same type filtering of %who can be applied here. |
|
822 | 821 | |
|
823 | 822 | For all variables, the type is printed. Additionally it prints: |
|
824 | 823 | |
|
825 | 824 | - For {},[],(): their length. |
|
826 | 825 | |
|
827 | 826 | - For numpy arrays, a summary with shape, number of |
|
828 | 827 | elements, typecode and size in memory. |
|
829 | 828 | |
|
830 | 829 | - Everything else: a string representation, snipping their middle if |
|
831 | 830 | too long. |
|
832 | 831 | |
|
833 | 832 | Examples |
|
834 | 833 | -------- |
|
835 | 834 | |
|
836 | 835 | Define two variables and list them with whos:: |
|
837 | 836 | |
|
838 | 837 | In [1]: alpha = 123 |
|
839 | 838 | |
|
840 | 839 | In [2]: beta = 'test' |
|
841 | 840 | |
|
842 | 841 | In [3]: %whos |
|
843 | 842 | Variable Type Data/Info |
|
844 | 843 | -------------------------------- |
|
845 | 844 | alpha int 123 |
|
846 | 845 | beta str test |
|
847 | 846 | """ |
|
848 | 847 | |
|
849 | 848 | varnames = self.magic_who_ls(parameter_s) |
|
850 | 849 | if not varnames: |
|
851 | 850 | if parameter_s: |
|
852 | 851 | print 'No variables match your requested type.' |
|
853 | 852 | else: |
|
854 | 853 | print 'Interactive namespace is empty.' |
|
855 | 854 | return |
|
856 | 855 | |
|
857 | 856 | # if we have variables, move on... |
|
858 | 857 | |
|
859 | 858 | # for these types, show len() instead of data: |
|
860 | 859 | seq_types = ['dict', 'list', 'tuple'] |
|
861 | 860 | |
|
862 | 861 | # for numpy/Numeric arrays, display summary info |
|
863 | 862 | try: |
|
864 | 863 | import numpy |
|
865 | 864 | except ImportError: |
|
866 | 865 | ndarray_type = None |
|
867 | 866 | else: |
|
868 | 867 | ndarray_type = numpy.ndarray.__name__ |
|
869 | 868 | try: |
|
870 | 869 | import Numeric |
|
871 | 870 | except ImportError: |
|
872 | 871 | array_type = None |
|
873 | 872 | else: |
|
874 | 873 | array_type = Numeric.ArrayType.__name__ |
|
875 | 874 | |
|
876 | 875 | # Find all variable names and types so we can figure out column sizes |
|
877 | 876 | def get_vars(i): |
|
878 | 877 | return self.shell.user_ns[i] |
|
879 | 878 | |
|
880 | 879 | # some types are well known and can be shorter |
|
881 | 880 | abbrevs = {'IPython.core.macro.Macro' : 'Macro'} |
|
882 | 881 | def type_name(v): |
|
883 | 882 | tn = type(v).__name__ |
|
884 | 883 | return abbrevs.get(tn,tn) |
|
885 | 884 | |
|
886 | 885 | varlist = map(get_vars,varnames) |
|
887 | 886 | |
|
888 | 887 | typelist = [] |
|
889 | 888 | for vv in varlist: |
|
890 | 889 | tt = type_name(vv) |
|
891 | 890 | |
|
892 | 891 | if tt=='instance': |
|
893 | 892 | typelist.append( abbrevs.get(str(vv.__class__), |
|
894 | 893 | str(vv.__class__))) |
|
895 | 894 | else: |
|
896 | 895 | typelist.append(tt) |
|
897 | 896 | |
|
898 | 897 | # column labels and # of spaces as separator |
|
899 | 898 | varlabel = 'Variable' |
|
900 | 899 | typelabel = 'Type' |
|
901 | 900 | datalabel = 'Data/Info' |
|
902 | 901 | colsep = 3 |
|
903 | 902 | # variable format strings |
|
904 | 903 | vformat = "{0:<{varwidth}}{1:<{typewidth}}" |
|
905 | 904 | aformat = "%s: %s elems, type `%s`, %s bytes" |
|
906 | 905 | # find the size of the columns to format the output nicely |
|
907 | 906 | varwidth = max(max(map(len,varnames)), len(varlabel)) + colsep |
|
908 | 907 | typewidth = max(max(map(len,typelist)), len(typelabel)) + colsep |
|
909 | 908 | # table header |
|
910 | 909 | print varlabel.ljust(varwidth) + typelabel.ljust(typewidth) + \ |
|
911 | 910 | ' '+datalabel+'\n' + '-'*(varwidth+typewidth+len(datalabel)+1) |
|
912 | 911 | # and the table itself |
|
913 | 912 | kb = 1024 |
|
914 | 913 | Mb = 1048576 # kb**2 |
|
915 | 914 | for vname,var,vtype in zip(varnames,varlist,typelist): |
|
916 | 915 | print vformat.format(vname, vtype, varwidth=varwidth, typewidth=typewidth), |
|
917 | 916 | if vtype in seq_types: |
|
918 | 917 | print "n="+str(len(var)) |
|
919 | 918 | elif vtype in [array_type,ndarray_type]: |
|
920 | 919 | vshape = str(var.shape).replace(',','').replace(' ','x')[1:-1] |
|
921 | 920 | if vtype==ndarray_type: |
|
922 | 921 | # numpy |
|
923 | 922 | vsize = var.size |
|
924 | 923 | vbytes = vsize*var.itemsize |
|
925 | 924 | vdtype = var.dtype |
|
926 | 925 | else: |
|
927 | 926 | # Numeric |
|
928 | 927 | vsize = Numeric.size(var) |
|
929 | 928 | vbytes = vsize*var.itemsize() |
|
930 | 929 | vdtype = var.typecode() |
|
931 | 930 | |
|
932 | 931 | if vbytes < 100000: |
|
933 | 932 | print aformat % (vshape,vsize,vdtype,vbytes) |
|
934 | 933 | else: |
|
935 | 934 | print aformat % (vshape,vsize,vdtype,vbytes), |
|
936 | 935 | if vbytes < Mb: |
|
937 | 936 | print '(%s kb)' % (vbytes/kb,) |
|
938 | 937 | else: |
|
939 | 938 | print '(%s Mb)' % (vbytes/Mb,) |
|
940 | 939 | else: |
|
941 | 940 | try: |
|
942 | 941 | vstr = str(var) |
|
943 | 942 | except UnicodeEncodeError: |
|
944 | 943 | vstr = unicode(var).encode(sys.getdefaultencoding(), |
|
945 | 944 | 'backslashreplace') |
|
946 | 945 | vstr = vstr.replace('\n','\\n') |
|
947 | 946 | if len(vstr) < 50: |
|
948 | 947 | print vstr |
|
949 | 948 | else: |
|
950 | 949 | print vstr[:25] + "<...>" + vstr[-25:] |
|
951 | 950 | |
|
952 | 951 | def magic_reset(self, parameter_s=''): |
|
953 | 952 | """Resets the namespace by removing all names defined by the user. |
|
954 | 953 | |
|
955 | 954 | Parameters |
|
956 | 955 | ---------- |
|
957 | 956 | -f : force reset without asking for confirmation. |
|
958 | 957 | |
|
959 | 958 | -s : 'Soft' reset: Only clears your namespace, leaving history intact. |
|
960 | 959 | References to objects may be kept. By default (without this option), |
|
961 | 960 | we do a 'hard' reset, giving you a new session and removing all |
|
962 | 961 | references to objects from the current session. |
|
963 | 962 | |
|
964 | 963 | Examples |
|
965 | 964 | -------- |
|
966 | 965 | In [6]: a = 1 |
|
967 | 966 | |
|
968 | 967 | In [7]: a |
|
969 | 968 | Out[7]: 1 |
|
970 | 969 | |
|
971 | 970 | In [8]: 'a' in _ip.user_ns |
|
972 | 971 | Out[8]: True |
|
973 | 972 | |
|
974 | 973 | In [9]: %reset -f |
|
975 | 974 | |
|
976 | 975 | In [1]: 'a' in _ip.user_ns |
|
977 | 976 | Out[1]: False |
|
978 | 977 | """ |
|
979 | 978 | opts, args = self.parse_options(parameter_s,'sf') |
|
980 | 979 | if 'f' in opts: |
|
981 | 980 | ans = True |
|
982 | 981 | else: |
|
983 | 982 | ans = self.shell.ask_yes_no( |
|
984 | 983 | "Once deleted, variables cannot be recovered. Proceed (y/[n])? ") |
|
985 | 984 | if not ans: |
|
986 | 985 | print 'Nothing done.' |
|
987 | 986 | return |
|
988 | 987 | |
|
989 | 988 | if 's' in opts: # Soft reset |
|
990 | 989 | user_ns = self.shell.user_ns |
|
991 | 990 | for i in self.magic_who_ls(): |
|
992 | 991 | del(user_ns[i]) |
|
993 | 992 | |
|
994 | 993 | else: # Hard reset |
|
995 | 994 | self.shell.reset(new_session = False) |
|
996 | 995 | |
|
997 | 996 | |
|
998 | 997 | |
|
999 | 998 | def magic_reset_selective(self, parameter_s=''): |
|
1000 | 999 | """Resets the namespace by removing names defined by the user. |
|
1001 | 1000 | |
|
1002 | 1001 | Input/Output history are left around in case you need them. |
|
1003 | 1002 | |
|
1004 | 1003 | %reset_selective [-f] regex |
|
1005 | 1004 | |
|
1006 | 1005 | No action is taken if regex is not included |
|
1007 | 1006 | |
|
1008 | 1007 | Options |
|
1009 | 1008 | -f : force reset without asking for confirmation. |
|
1010 | 1009 | |
|
1011 | 1010 | Examples |
|
1012 | 1011 | -------- |
|
1013 | 1012 | |
|
1014 | 1013 | We first fully reset the namespace so your output looks identical to |
|
1015 | 1014 | this example for pedagogical reasons; in practice you do not need a |
|
1016 | 1015 | full reset. |
|
1017 | 1016 | |
|
1018 | 1017 | In [1]: %reset -f |
|
1019 | 1018 | |
|
1020 | 1019 | Now, with a clean namespace we can make a few variables and use |
|
1021 | 1020 | %reset_selective to only delete names that match our regexp: |
|
1022 | 1021 | |
|
1023 | 1022 | In [2]: a=1; b=2; c=3; b1m=4; b2m=5; b3m=6; b4m=7; b2s=8 |
|
1024 | 1023 | |
|
1025 | 1024 | In [3]: who_ls |
|
1026 | 1025 | Out[3]: ['a', 'b', 'b1m', 'b2m', 'b2s', 'b3m', 'b4m', 'c'] |
|
1027 | 1026 | |
|
1028 | 1027 | In [4]: %reset_selective -f b[2-3]m |
|
1029 | 1028 | |
|
1030 | 1029 | In [5]: who_ls |
|
1031 | 1030 | Out[5]: ['a', 'b', 'b1m', 'b2s', 'b4m', 'c'] |
|
1032 | 1031 | |
|
1033 | 1032 | In [6]: %reset_selective -f d |
|
1034 | 1033 | |
|
1035 | 1034 | In [7]: who_ls |
|
1036 | 1035 | Out[7]: ['a', 'b', 'b1m', 'b2s', 'b4m', 'c'] |
|
1037 | 1036 | |
|
1038 | 1037 | In [8]: %reset_selective -f c |
|
1039 | 1038 | |
|
1040 | 1039 | In [9]: who_ls |
|
1041 | 1040 | Out[9]: ['a', 'b', 'b1m', 'b2s', 'b4m'] |
|
1042 | 1041 | |
|
1043 | 1042 | In [10]: %reset_selective -f b |
|
1044 | 1043 | |
|
1045 | 1044 | In [11]: who_ls |
|
1046 | 1045 | Out[11]: ['a'] |
|
1047 | 1046 | """ |
|
1048 | 1047 | |
|
1049 | 1048 | opts, regex = self.parse_options(parameter_s,'f') |
|
1050 | 1049 | |
|
1051 | 1050 | if opts.has_key('f'): |
|
1052 | 1051 | ans = True |
|
1053 | 1052 | else: |
|
1054 | 1053 | ans = self.shell.ask_yes_no( |
|
1055 | 1054 | "Once deleted, variables cannot be recovered. Proceed (y/[n])? ") |
|
1056 | 1055 | if not ans: |
|
1057 | 1056 | print 'Nothing done.' |
|
1058 | 1057 | return |
|
1059 | 1058 | user_ns = self.shell.user_ns |
|
1060 | 1059 | if not regex: |
|
1061 | 1060 | print 'No regex pattern specified. Nothing done.' |
|
1062 | 1061 | return |
|
1063 | 1062 | else: |
|
1064 | 1063 | try: |
|
1065 | 1064 | m = re.compile(regex) |
|
1066 | 1065 | except TypeError: |
|
1067 | 1066 | raise TypeError('regex must be a string or compiled pattern') |
|
1068 | 1067 | for i in self.magic_who_ls(): |
|
1069 | 1068 | if m.search(i): |
|
1070 | 1069 | del(user_ns[i]) |
|
1071 | 1070 | |
|
1072 | 1071 | def magic_xdel(self, parameter_s=''): |
|
1073 | 1072 | """Delete a variable, trying to clear it from anywhere that |
|
1074 | 1073 | IPython's machinery has references to it. By default, this uses |
|
1075 | 1074 | the identity of the named object in the user namespace to remove |
|
1076 | 1075 | references held under other names. The object is also removed |
|
1077 | 1076 | from the output history. |
|
1078 | 1077 | |
|
1079 | 1078 | Options |
|
1080 | 1079 | -n : Delete the specified name from all namespaces, without |
|
1081 | 1080 | checking their identity. |
|
1082 | 1081 | """ |
|
1083 | 1082 | opts, varname = self.parse_options(parameter_s,'n') |
|
1084 | 1083 | try: |
|
1085 | 1084 | self.shell.del_var(varname, ('n' in opts)) |
|
1086 | 1085 | except (NameError, ValueError) as e: |
|
1087 | 1086 | print type(e).__name__ +": "+ str(e) |
|
1088 | 1087 | |
|
1089 | 1088 | def magic_logstart(self,parameter_s=''): |
|
1090 | 1089 | """Start logging anywhere in a session. |
|
1091 | 1090 | |
|
1092 | 1091 | %logstart [-o|-r|-t] [log_name [log_mode]] |
|
1093 | 1092 | |
|
1094 | 1093 | If no name is given, it defaults to a file named 'ipython_log.py' in your |
|
1095 | 1094 | current directory, in 'rotate' mode (see below). |
|
1096 | 1095 | |
|
1097 | 1096 | '%logstart name' saves to file 'name' in 'backup' mode. It saves your |
|
1098 | 1097 | history up to that point and then continues logging. |
|
1099 | 1098 | |
|
1100 | 1099 | %logstart takes a second optional parameter: logging mode. This can be one |
|
1101 | 1100 | of (note that the modes are given unquoted):\\ |
|
1102 | 1101 | append: well, that says it.\\ |
|
1103 | 1102 | backup: rename (if exists) to name~ and start name.\\ |
|
1104 | 1103 | global: single logfile in your home dir, appended to.\\ |
|
1105 | 1104 | over : overwrite existing log.\\ |
|
1106 | 1105 | rotate: create rotating logs name.1~, name.2~, etc. |
|
1107 | 1106 | |
|
1108 | 1107 | Options: |
|
1109 | 1108 | |
|
1110 | 1109 | -o: log also IPython's output. In this mode, all commands which |
|
1111 | 1110 | generate an Out[NN] prompt are recorded to the logfile, right after |
|
1112 | 1111 | their corresponding input line. The output lines are always |
|
1113 | 1112 | prepended with a '#[Out]# ' marker, so that the log remains valid |
|
1114 | 1113 | Python code. |
|
1115 | 1114 | |
|
1116 | 1115 | Since this marker is always the same, filtering only the output from |
|
1117 | 1116 | a log is very easy, using for example a simple awk call: |
|
1118 | 1117 | |
|
1119 | 1118 | awk -F'#\\[Out\\]# ' '{if($2) {print $2}}' ipython_log.py |
|
1120 | 1119 | |
|
1121 | 1120 | -r: log 'raw' input. Normally, IPython's logs contain the processed |
|
1122 | 1121 | input, so that user lines are logged in their final form, converted |
|
1123 | 1122 | into valid Python. For example, %Exit is logged as |
|
1124 | 1123 | '_ip.magic("Exit"). If the -r flag is given, all input is logged |
|
1125 | 1124 | exactly as typed, with no transformations applied. |
|
1126 | 1125 | |
|
1127 | 1126 | -t: put timestamps before each input line logged (these are put in |
|
1128 | 1127 | comments).""" |
|
1129 | 1128 | |
|
1130 | 1129 | opts,par = self.parse_options(parameter_s,'ort') |
|
1131 | 1130 | log_output = 'o' in opts |
|
1132 | 1131 | log_raw_input = 'r' in opts |
|
1133 | 1132 | timestamp = 't' in opts |
|
1134 | 1133 | |
|
1135 | 1134 | logger = self.shell.logger |
|
1136 | 1135 | |
|
1137 | 1136 | # if no args are given, the defaults set in the logger constructor by |
|
1138 | 1137 | # ipytohn remain valid |
|
1139 | 1138 | if par: |
|
1140 | 1139 | try: |
|
1141 | 1140 | logfname,logmode = par.split() |
|
1142 | 1141 | except: |
|
1143 | 1142 | logfname = par |
|
1144 | 1143 | logmode = 'backup' |
|
1145 | 1144 | else: |
|
1146 | 1145 | logfname = logger.logfname |
|
1147 | 1146 | logmode = logger.logmode |
|
1148 | 1147 | # put logfname into rc struct as if it had been called on the command |
|
1149 | 1148 | # line, so it ends up saved in the log header Save it in case we need |
|
1150 | 1149 | # to restore it... |
|
1151 | 1150 | old_logfile = self.shell.logfile |
|
1152 | 1151 | if logfname: |
|
1153 | 1152 | logfname = os.path.expanduser(logfname) |
|
1154 | 1153 | self.shell.logfile = logfname |
|
1155 | 1154 | |
|
1156 | 1155 | loghead = '# IPython log file\n\n' |
|
1157 | 1156 | try: |
|
1158 | 1157 | started = logger.logstart(logfname,loghead,logmode, |
|
1159 | 1158 | log_output,timestamp,log_raw_input) |
|
1160 | 1159 | except: |
|
1161 | 1160 | self.shell.logfile = old_logfile |
|
1162 | 1161 | warn("Couldn't start log: %s" % sys.exc_info()[1]) |
|
1163 | 1162 | else: |
|
1164 | 1163 | # log input history up to this point, optionally interleaving |
|
1165 | 1164 | # output if requested |
|
1166 | 1165 | |
|
1167 | 1166 | if timestamp: |
|
1168 | 1167 | # disable timestamping for the previous history, since we've |
|
1169 | 1168 | # lost those already (no time machine here). |
|
1170 | 1169 | logger.timestamp = False |
|
1171 | 1170 | |
|
1172 | 1171 | if log_raw_input: |
|
1173 | 1172 | input_hist = self.shell.history_manager.input_hist_raw |
|
1174 | 1173 | else: |
|
1175 | 1174 | input_hist = self.shell.history_manager.input_hist_parsed |
|
1176 | 1175 | |
|
1177 | 1176 | if log_output: |
|
1178 | 1177 | log_write = logger.log_write |
|
1179 | 1178 | output_hist = self.shell.history_manager.output_hist |
|
1180 | 1179 | for n in range(1,len(input_hist)-1): |
|
1181 | 1180 | log_write(input_hist[n].rstrip() + '\n') |
|
1182 | 1181 | if n in output_hist: |
|
1183 | 1182 | log_write(repr(output_hist[n]),'output') |
|
1184 | 1183 | else: |
|
1185 | 1184 | logger.log_write('\n'.join(input_hist[1:])) |
|
1186 | 1185 | logger.log_write('\n') |
|
1187 | 1186 | if timestamp: |
|
1188 | 1187 | # re-enable timestamping |
|
1189 | 1188 | logger.timestamp = True |
|
1190 | 1189 | |
|
1191 | 1190 | print ('Activating auto-logging. ' |
|
1192 | 1191 | 'Current session state plus future input saved.') |
|
1193 | 1192 | logger.logstate() |
|
1194 | 1193 | |
|
1195 | 1194 | def magic_logstop(self,parameter_s=''): |
|
1196 | 1195 | """Fully stop logging and close log file. |
|
1197 | 1196 | |
|
1198 | 1197 | In order to start logging again, a new %logstart call needs to be made, |
|
1199 | 1198 | possibly (though not necessarily) with a new filename, mode and other |
|
1200 | 1199 | options.""" |
|
1201 | 1200 | self.logger.logstop() |
|
1202 | 1201 | |
|
1203 | 1202 | def magic_logoff(self,parameter_s=''): |
|
1204 | 1203 | """Temporarily stop logging. |
|
1205 | 1204 | |
|
1206 | 1205 | You must have previously started logging.""" |
|
1207 | 1206 | self.shell.logger.switch_log(0) |
|
1208 | 1207 | |
|
1209 | 1208 | def magic_logon(self,parameter_s=''): |
|
1210 | 1209 | """Restart logging. |
|
1211 | 1210 | |
|
1212 | 1211 | This function is for restarting logging which you've temporarily |
|
1213 | 1212 | stopped with %logoff. For starting logging for the first time, you |
|
1214 | 1213 | must use the %logstart function, which allows you to specify an |
|
1215 | 1214 | optional log filename.""" |
|
1216 | 1215 | |
|
1217 | 1216 | self.shell.logger.switch_log(1) |
|
1218 | 1217 | |
|
1219 | 1218 | def magic_logstate(self,parameter_s=''): |
|
1220 | 1219 | """Print the status of the logging system.""" |
|
1221 | 1220 | |
|
1222 | 1221 | self.shell.logger.logstate() |
|
1223 | 1222 | |
|
1224 | 1223 | def magic_pdb(self, parameter_s=''): |
|
1225 | 1224 | """Control the automatic calling of the pdb interactive debugger. |
|
1226 | 1225 | |
|
1227 | 1226 | Call as '%pdb on', '%pdb 1', '%pdb off' or '%pdb 0'. If called without |
|
1228 | 1227 | argument it works as a toggle. |
|
1229 | 1228 | |
|
1230 | 1229 | When an exception is triggered, IPython can optionally call the |
|
1231 | 1230 | interactive pdb debugger after the traceback printout. %pdb toggles |
|
1232 | 1231 | this feature on and off. |
|
1233 | 1232 | |
|
1234 | 1233 | The initial state of this feature is set in your ipythonrc |
|
1235 | 1234 | configuration file (the variable is called 'pdb'). |
|
1236 | 1235 | |
|
1237 | 1236 | If you want to just activate the debugger AFTER an exception has fired, |
|
1238 | 1237 | without having to type '%pdb on' and rerunning your code, you can use |
|
1239 | 1238 | the %debug magic.""" |
|
1240 | 1239 | |
|
1241 | 1240 | par = parameter_s.strip().lower() |
|
1242 | 1241 | |
|
1243 | 1242 | if par: |
|
1244 | 1243 | try: |
|
1245 | 1244 | new_pdb = {'off':0,'0':0,'on':1,'1':1}[par] |
|
1246 | 1245 | except KeyError: |
|
1247 | 1246 | print ('Incorrect argument. Use on/1, off/0, ' |
|
1248 | 1247 | 'or nothing for a toggle.') |
|
1249 | 1248 | return |
|
1250 | 1249 | else: |
|
1251 | 1250 | # toggle |
|
1252 | 1251 | new_pdb = not self.shell.call_pdb |
|
1253 | 1252 | |
|
1254 | 1253 | # set on the shell |
|
1255 | 1254 | self.shell.call_pdb = new_pdb |
|
1256 | 1255 | print 'Automatic pdb calling has been turned',on_off(new_pdb) |
|
1257 | 1256 | |
|
1258 | 1257 | def magic_debug(self, parameter_s=''): |
|
1259 | 1258 | """Activate the interactive debugger in post-mortem mode. |
|
1260 | 1259 | |
|
1261 | 1260 | If an exception has just occurred, this lets you inspect its stack |
|
1262 | 1261 | frames interactively. Note that this will always work only on the last |
|
1263 | 1262 | traceback that occurred, so you must call this quickly after an |
|
1264 | 1263 | exception that you wish to inspect has fired, because if another one |
|
1265 | 1264 | occurs, it clobbers the previous one. |
|
1266 | 1265 | |
|
1267 | 1266 | If you want IPython to automatically do this on every exception, see |
|
1268 | 1267 | the %pdb magic for more details. |
|
1269 | 1268 | """ |
|
1270 | 1269 | self.shell.debugger(force=True) |
|
1271 | 1270 | |
|
1272 | 1271 | @skip_doctest |
|
1273 | 1272 | def magic_prun(self, parameter_s ='',user_mode=1, |
|
1274 | 1273 | opts=None,arg_lst=None,prog_ns=None): |
|
1275 | 1274 | |
|
1276 | 1275 | """Run a statement through the python code profiler. |
|
1277 | 1276 | |
|
1278 | 1277 | Usage: |
|
1279 | 1278 | %prun [options] statement |
|
1280 | 1279 | |
|
1281 | 1280 | The given statement (which doesn't require quote marks) is run via the |
|
1282 | 1281 | python profiler in a manner similar to the profile.run() function. |
|
1283 | 1282 | Namespaces are internally managed to work correctly; profile.run |
|
1284 | 1283 | cannot be used in IPython because it makes certain assumptions about |
|
1285 | 1284 | namespaces which do not hold under IPython. |
|
1286 | 1285 | |
|
1287 | 1286 | Options: |
|
1288 | 1287 | |
|
1289 | 1288 | -l <limit>: you can place restrictions on what or how much of the |
|
1290 | 1289 | profile gets printed. The limit value can be: |
|
1291 | 1290 | |
|
1292 | 1291 | * A string: only information for function names containing this string |
|
1293 | 1292 | is printed. |
|
1294 | 1293 | |
|
1295 | 1294 | * An integer: only these many lines are printed. |
|
1296 | 1295 | |
|
1297 | 1296 | * A float (between 0 and 1): this fraction of the report is printed |
|
1298 | 1297 | (for example, use a limit of 0.4 to see the topmost 40% only). |
|
1299 | 1298 | |
|
1300 | 1299 | You can combine several limits with repeated use of the option. For |
|
1301 | 1300 | example, '-l __init__ -l 5' will print only the topmost 5 lines of |
|
1302 | 1301 | information about class constructors. |
|
1303 | 1302 | |
|
1304 | 1303 | -r: return the pstats.Stats object generated by the profiling. This |
|
1305 | 1304 | object has all the information about the profile in it, and you can |
|
1306 | 1305 | later use it for further analysis or in other functions. |
|
1307 | 1306 | |
|
1308 | 1307 | -s <key>: sort profile by given key. You can provide more than one key |
|
1309 | 1308 | by using the option several times: '-s key1 -s key2 -s key3...'. The |
|
1310 | 1309 | default sorting key is 'time'. |
|
1311 | 1310 | |
|
1312 | 1311 | The following is copied verbatim from the profile documentation |
|
1313 | 1312 | referenced below: |
|
1314 | 1313 | |
|
1315 | 1314 | When more than one key is provided, additional keys are used as |
|
1316 | 1315 | secondary criteria when the there is equality in all keys selected |
|
1317 | 1316 | before them. |
|
1318 | 1317 | |
|
1319 | 1318 | Abbreviations can be used for any key names, as long as the |
|
1320 | 1319 | abbreviation is unambiguous. The following are the keys currently |
|
1321 | 1320 | defined: |
|
1322 | 1321 | |
|
1323 | 1322 | Valid Arg Meaning |
|
1324 | 1323 | "calls" call count |
|
1325 | 1324 | "cumulative" cumulative time |
|
1326 | 1325 | "file" file name |
|
1327 | 1326 | "module" file name |
|
1328 | 1327 | "pcalls" primitive call count |
|
1329 | 1328 | "line" line number |
|
1330 | 1329 | "name" function name |
|
1331 | 1330 | "nfl" name/file/line |
|
1332 | 1331 | "stdname" standard name |
|
1333 | 1332 | "time" internal time |
|
1334 | 1333 | |
|
1335 | 1334 | Note that all sorts on statistics are in descending order (placing |
|
1336 | 1335 | most time consuming items first), where as name, file, and line number |
|
1337 | 1336 | searches are in ascending order (i.e., alphabetical). The subtle |
|
1338 | 1337 | distinction between "nfl" and "stdname" is that the standard name is a |
|
1339 | 1338 | sort of the name as printed, which means that the embedded line |
|
1340 | 1339 | numbers get compared in an odd way. For example, lines 3, 20, and 40 |
|
1341 | 1340 | would (if the file names were the same) appear in the string order |
|
1342 | 1341 | "20" "3" and "40". In contrast, "nfl" does a numeric compare of the |
|
1343 | 1342 | line numbers. In fact, sort_stats("nfl") is the same as |
|
1344 | 1343 | sort_stats("name", "file", "line"). |
|
1345 | 1344 | |
|
1346 | 1345 | -T <filename>: save profile results as shown on screen to a text |
|
1347 | 1346 | file. The profile is still shown on screen. |
|
1348 | 1347 | |
|
1349 | 1348 | -D <filename>: save (via dump_stats) profile statistics to given |
|
1350 | 1349 | filename. This data is in a format understod by the pstats module, and |
|
1351 | 1350 | is generated by a call to the dump_stats() method of profile |
|
1352 | 1351 | objects. The profile is still shown on screen. |
|
1353 | 1352 | |
|
1354 | 1353 | If you want to run complete programs under the profiler's control, use |
|
1355 | 1354 | '%run -p [prof_opts] filename.py [args to program]' where prof_opts |
|
1356 | 1355 | contains profiler specific options as described here. |
|
1357 | 1356 | |
|
1358 | 1357 | You can read the complete documentation for the profile module with:: |
|
1359 | 1358 | |
|
1360 | 1359 | In [1]: import profile; profile.help() |
|
1361 | 1360 | """ |
|
1362 | 1361 | |
|
1363 | 1362 | opts_def = Struct(D=[''],l=[],s=['time'],T=['']) |
|
1364 | 1363 | # protect user quote marks |
|
1365 | 1364 | parameter_s = parameter_s.replace('"',r'\"').replace("'",r"\'") |
|
1366 | 1365 | |
|
1367 | 1366 | if user_mode: # regular user call |
|
1368 | 1367 | opts,arg_str = self.parse_options(parameter_s,'D:l:rs:T:', |
|
1369 | 1368 | list_all=1) |
|
1370 | 1369 | namespace = self.shell.user_ns |
|
1371 | 1370 | else: # called to run a program by %run -p |
|
1372 | 1371 | try: |
|
1373 | 1372 | filename = get_py_filename(arg_lst[0]) |
|
1374 | 1373 | except IOError,msg: |
|
1375 | 1374 | error(msg) |
|
1376 | 1375 | return |
|
1377 | 1376 | |
|
1378 | 1377 | arg_str = 'execfile(filename,prog_ns)' |
|
1379 | 1378 | namespace = locals() |
|
1380 | 1379 | |
|
1381 | 1380 | opts.merge(opts_def) |
|
1382 | 1381 | |
|
1383 | 1382 | prof = profile.Profile() |
|
1384 | 1383 | try: |
|
1385 | 1384 | prof = prof.runctx(arg_str,namespace,namespace) |
|
1386 | 1385 | sys_exit = '' |
|
1387 | 1386 | except SystemExit: |
|
1388 | 1387 | sys_exit = """*** SystemExit exception caught in code being profiled.""" |
|
1389 | 1388 | |
|
1390 | 1389 | stats = pstats.Stats(prof).strip_dirs().sort_stats(*opts.s) |
|
1391 | 1390 | |
|
1392 | 1391 | lims = opts.l |
|
1393 | 1392 | if lims: |
|
1394 | 1393 | lims = [] # rebuild lims with ints/floats/strings |
|
1395 | 1394 | for lim in opts.l: |
|
1396 | 1395 | try: |
|
1397 | 1396 | lims.append(int(lim)) |
|
1398 | 1397 | except ValueError: |
|
1399 | 1398 | try: |
|
1400 | 1399 | lims.append(float(lim)) |
|
1401 | 1400 | except ValueError: |
|
1402 | 1401 | lims.append(lim) |
|
1403 | 1402 | |
|
1404 | 1403 | # Trap output. |
|
1405 | 1404 | stdout_trap = StringIO() |
|
1406 | 1405 | |
|
1407 | 1406 | if hasattr(stats,'stream'): |
|
1408 | 1407 | # In newer versions of python, the stats object has a 'stream' |
|
1409 | 1408 | # attribute to write into. |
|
1410 | 1409 | stats.stream = stdout_trap |
|
1411 | 1410 | stats.print_stats(*lims) |
|
1412 | 1411 | else: |
|
1413 | 1412 | # For older versions, we manually redirect stdout during printing |
|
1414 | 1413 | sys_stdout = sys.stdout |
|
1415 | 1414 | try: |
|
1416 | 1415 | sys.stdout = stdout_trap |
|
1417 | 1416 | stats.print_stats(*lims) |
|
1418 | 1417 | finally: |
|
1419 | 1418 | sys.stdout = sys_stdout |
|
1420 | 1419 | |
|
1421 | 1420 | output = stdout_trap.getvalue() |
|
1422 | 1421 | output = output.rstrip() |
|
1423 | 1422 | |
|
1424 | 1423 | page.page(output) |
|
1425 | 1424 | print sys_exit, |
|
1426 | 1425 | |
|
1427 | 1426 | dump_file = opts.D[0] |
|
1428 | 1427 | text_file = opts.T[0] |
|
1429 | 1428 | if dump_file: |
|
1430 | 1429 | prof.dump_stats(dump_file) |
|
1431 | 1430 | print '\n*** Profile stats marshalled to file',\ |
|
1432 | 1431 | `dump_file`+'.',sys_exit |
|
1433 | 1432 | if text_file: |
|
1434 | 1433 | pfile = file(text_file,'w') |
|
1435 | 1434 | pfile.write(output) |
|
1436 | 1435 | pfile.close() |
|
1437 | 1436 | print '\n*** Profile printout saved to text file',\ |
|
1438 | 1437 | `text_file`+'.',sys_exit |
|
1439 | 1438 | |
|
1440 | 1439 | if opts.has_key('r'): |
|
1441 | 1440 | return stats |
|
1442 | 1441 | else: |
|
1443 | 1442 | return None |
|
1444 | 1443 | |
|
1445 | 1444 | @skip_doctest |
|
1446 | 1445 | def magic_run(self, parameter_s ='',runner=None, |
|
1447 | 1446 | file_finder=get_py_filename): |
|
1448 | 1447 | """Run the named file inside IPython as a program. |
|
1449 | 1448 | |
|
1450 | 1449 | Usage:\\ |
|
1451 | 1450 | %run [-n -i -t [-N<N>] -d [-b<N>] -p [profile options]] file [args] |
|
1452 | 1451 | |
|
1453 | 1452 | Parameters after the filename are passed as command-line arguments to |
|
1454 | 1453 | the program (put in sys.argv). Then, control returns to IPython's |
|
1455 | 1454 | prompt. |
|
1456 | 1455 | |
|
1457 | 1456 | This is similar to running at a system prompt:\\ |
|
1458 | 1457 | $ python file args\\ |
|
1459 | 1458 | but with the advantage of giving you IPython's tracebacks, and of |
|
1460 | 1459 | loading all variables into your interactive namespace for further use |
|
1461 | 1460 | (unless -p is used, see below). |
|
1462 | 1461 | |
|
1463 | 1462 | The file is executed in a namespace initially consisting only of |
|
1464 | 1463 | __name__=='__main__' and sys.argv constructed as indicated. It thus |
|
1465 | 1464 | sees its environment as if it were being run as a stand-alone program |
|
1466 | 1465 | (except for sharing global objects such as previously imported |
|
1467 | 1466 | modules). But after execution, the IPython interactive namespace gets |
|
1468 | 1467 | updated with all variables defined in the program (except for __name__ |
|
1469 | 1468 | and sys.argv). This allows for very convenient loading of code for |
|
1470 | 1469 | interactive work, while giving each program a 'clean sheet' to run in. |
|
1471 | 1470 | |
|
1472 | 1471 | Options: |
|
1473 | 1472 | |
|
1474 | 1473 | -n: __name__ is NOT set to '__main__', but to the running file's name |
|
1475 | 1474 | without extension (as python does under import). This allows running |
|
1476 | 1475 | scripts and reloading the definitions in them without calling code |
|
1477 | 1476 | protected by an ' if __name__ == "__main__" ' clause. |
|
1478 | 1477 | |
|
1479 | 1478 | -i: run the file in IPython's namespace instead of an empty one. This |
|
1480 | 1479 | is useful if you are experimenting with code written in a text editor |
|
1481 | 1480 | which depends on variables defined interactively. |
|
1482 | 1481 | |
|
1483 | 1482 | -e: ignore sys.exit() calls or SystemExit exceptions in the script |
|
1484 | 1483 | being run. This is particularly useful if IPython is being used to |
|
1485 | 1484 | run unittests, which always exit with a sys.exit() call. In such |
|
1486 | 1485 | cases you are interested in the output of the test results, not in |
|
1487 | 1486 | seeing a traceback of the unittest module. |
|
1488 | 1487 | |
|
1489 | 1488 | -t: print timing information at the end of the run. IPython will give |
|
1490 | 1489 | you an estimated CPU time consumption for your script, which under |
|
1491 | 1490 | Unix uses the resource module to avoid the wraparound problems of |
|
1492 | 1491 | time.clock(). Under Unix, an estimate of time spent on system tasks |
|
1493 | 1492 | is also given (for Windows platforms this is reported as 0.0). |
|
1494 | 1493 | |
|
1495 | 1494 | If -t is given, an additional -N<N> option can be given, where <N> |
|
1496 | 1495 | must be an integer indicating how many times you want the script to |
|
1497 | 1496 | run. The final timing report will include total and per run results. |
|
1498 | 1497 | |
|
1499 | 1498 | For example (testing the script uniq_stable.py): |
|
1500 | 1499 | |
|
1501 | 1500 | In [1]: run -t uniq_stable |
|
1502 | 1501 | |
|
1503 | 1502 | IPython CPU timings (estimated):\\ |
|
1504 | 1503 | User : 0.19597 s.\\ |
|
1505 | 1504 | System: 0.0 s.\\ |
|
1506 | 1505 | |
|
1507 | 1506 | In [2]: run -t -N5 uniq_stable |
|
1508 | 1507 | |
|
1509 | 1508 | IPython CPU timings (estimated):\\ |
|
1510 | 1509 | Total runs performed: 5\\ |
|
1511 | 1510 | Times : Total Per run\\ |
|
1512 | 1511 | User : 0.910862 s, 0.1821724 s.\\ |
|
1513 | 1512 | System: 0.0 s, 0.0 s. |
|
1514 | 1513 | |
|
1515 | 1514 | -d: run your program under the control of pdb, the Python debugger. |
|
1516 | 1515 | This allows you to execute your program step by step, watch variables, |
|
1517 | 1516 | etc. Internally, what IPython does is similar to calling: |
|
1518 | 1517 | |
|
1519 | 1518 | pdb.run('execfile("YOURFILENAME")') |
|
1520 | 1519 | |
|
1521 | 1520 | with a breakpoint set on line 1 of your file. You can change the line |
|
1522 | 1521 | number for this automatic breakpoint to be <N> by using the -bN option |
|
1523 | 1522 | (where N must be an integer). For example: |
|
1524 | 1523 | |
|
1525 | 1524 | %run -d -b40 myscript |
|
1526 | 1525 | |
|
1527 | 1526 | will set the first breakpoint at line 40 in myscript.py. Note that |
|
1528 | 1527 | the first breakpoint must be set on a line which actually does |
|
1529 | 1528 | something (not a comment or docstring) for it to stop execution. |
|
1530 | 1529 | |
|
1531 | 1530 | When the pdb debugger starts, you will see a (Pdb) prompt. You must |
|
1532 | 1531 | first enter 'c' (without qoutes) to start execution up to the first |
|
1533 | 1532 | breakpoint. |
|
1534 | 1533 | |
|
1535 | 1534 | Entering 'help' gives information about the use of the debugger. You |
|
1536 | 1535 | can easily see pdb's full documentation with "import pdb;pdb.help()" |
|
1537 | 1536 | at a prompt. |
|
1538 | 1537 | |
|
1539 | 1538 | -p: run program under the control of the Python profiler module (which |
|
1540 | 1539 | prints a detailed report of execution times, function calls, etc). |
|
1541 | 1540 | |
|
1542 | 1541 | You can pass other options after -p which affect the behavior of the |
|
1543 | 1542 | profiler itself. See the docs for %prun for details. |
|
1544 | 1543 | |
|
1545 | 1544 | In this mode, the program's variables do NOT propagate back to the |
|
1546 | 1545 | IPython interactive namespace (because they remain in the namespace |
|
1547 | 1546 | where the profiler executes them). |
|
1548 | 1547 | |
|
1549 | 1548 | Internally this triggers a call to %prun, see its documentation for |
|
1550 | 1549 | details on the options available specifically for profiling. |
|
1551 | 1550 | |
|
1552 | 1551 | There is one special usage for which the text above doesn't apply: |
|
1553 | 1552 | if the filename ends with .ipy, the file is run as ipython script, |
|
1554 | 1553 | just as if the commands were written on IPython prompt. |
|
1555 | 1554 | """ |
|
1556 | 1555 | |
|
1557 | 1556 | # get arguments and set sys.argv for program to be run. |
|
1558 | 1557 | opts,arg_lst = self.parse_options(parameter_s,'nidtN:b:pD:l:rs:T:e', |
|
1559 | 1558 | mode='list',list_all=1) |
|
1560 | 1559 | |
|
1561 | 1560 | try: |
|
1562 | 1561 | filename = file_finder(arg_lst[0]) |
|
1563 | 1562 | except IndexError: |
|
1564 | 1563 | warn('you must provide at least a filename.') |
|
1565 | 1564 | print '\n%run:\n',oinspect.getdoc(self.magic_run) |
|
1566 | 1565 | return |
|
1567 | 1566 | except IOError,msg: |
|
1568 | 1567 | error(msg) |
|
1569 | 1568 | return |
|
1570 | 1569 | |
|
1571 | 1570 | if filename.lower().endswith('.ipy'): |
|
1572 | 1571 | self.shell.safe_execfile_ipy(filename) |
|
1573 | 1572 | return |
|
1574 | 1573 | |
|
1575 | 1574 | # Control the response to exit() calls made by the script being run |
|
1576 | 1575 | exit_ignore = opts.has_key('e') |
|
1577 | 1576 | |
|
1578 | 1577 | # Make sure that the running script gets a proper sys.argv as if it |
|
1579 | 1578 | # were run from a system shell. |
|
1580 | 1579 | save_argv = sys.argv # save it for later restoring |
|
1581 | 1580 | |
|
1582 | 1581 | # simulate shell expansion on arguments, at least tilde expansion |
|
1583 | 1582 | args = [ os.path.expanduser(a) for a in arg_lst[1:] ] |
|
1584 | 1583 | |
|
1585 | 1584 | sys.argv = [filename]+ args # put in the proper filename |
|
1586 | 1585 | |
|
1587 | 1586 | if opts.has_key('i'): |
|
1588 | 1587 | # Run in user's interactive namespace |
|
1589 | 1588 | prog_ns = self.shell.user_ns |
|
1590 | 1589 | __name__save = self.shell.user_ns['__name__'] |
|
1591 | 1590 | prog_ns['__name__'] = '__main__' |
|
1592 | 1591 | main_mod = self.shell.new_main_mod(prog_ns) |
|
1593 | 1592 | else: |
|
1594 | 1593 | # Run in a fresh, empty namespace |
|
1595 | 1594 | if opts.has_key('n'): |
|
1596 | 1595 | name = os.path.splitext(os.path.basename(filename))[0] |
|
1597 | 1596 | else: |
|
1598 | 1597 | name = '__main__' |
|
1599 | 1598 | |
|
1600 | 1599 | main_mod = self.shell.new_main_mod() |
|
1601 | 1600 | prog_ns = main_mod.__dict__ |
|
1602 | 1601 | prog_ns['__name__'] = name |
|
1603 | 1602 | |
|
1604 | 1603 | # Since '%run foo' emulates 'python foo.py' at the cmd line, we must |
|
1605 | 1604 | # set the __file__ global in the script's namespace |
|
1606 | 1605 | prog_ns['__file__'] = filename |
|
1607 | 1606 | |
|
1608 | 1607 | # pickle fix. See interactiveshell for an explanation. But we need to make sure |
|
1609 | 1608 | # that, if we overwrite __main__, we replace it at the end |
|
1610 | 1609 | main_mod_name = prog_ns['__name__'] |
|
1611 | 1610 | |
|
1612 | 1611 | if main_mod_name == '__main__': |
|
1613 | 1612 | restore_main = sys.modules['__main__'] |
|
1614 | 1613 | else: |
|
1615 | 1614 | restore_main = False |
|
1616 | 1615 | |
|
1617 | 1616 | # This needs to be undone at the end to prevent holding references to |
|
1618 | 1617 | # every single object ever created. |
|
1619 | 1618 | sys.modules[main_mod_name] = main_mod |
|
1620 | 1619 | |
|
1621 | 1620 | try: |
|
1622 | 1621 | stats = None |
|
1623 | 1622 | with self.readline_no_record: |
|
1624 | 1623 | if opts.has_key('p'): |
|
1625 | 1624 | stats = self.magic_prun('',0,opts,arg_lst,prog_ns) |
|
1626 | 1625 | else: |
|
1627 | 1626 | if opts.has_key('d'): |
|
1628 | 1627 | deb = debugger.Pdb(self.shell.colors) |
|
1629 | 1628 | # reset Breakpoint state, which is moronically kept |
|
1630 | 1629 | # in a class |
|
1631 | 1630 | bdb.Breakpoint.next = 1 |
|
1632 | 1631 | bdb.Breakpoint.bplist = {} |
|
1633 | 1632 | bdb.Breakpoint.bpbynumber = [None] |
|
1634 | 1633 | # Set an initial breakpoint to stop execution |
|
1635 | 1634 | maxtries = 10 |
|
1636 | 1635 | bp = int(opts.get('b',[1])[0]) |
|
1637 | 1636 | checkline = deb.checkline(filename,bp) |
|
1638 | 1637 | if not checkline: |
|
1639 | 1638 | for bp in range(bp+1,bp+maxtries+1): |
|
1640 | 1639 | if deb.checkline(filename,bp): |
|
1641 | 1640 | break |
|
1642 | 1641 | else: |
|
1643 | 1642 | msg = ("\nI failed to find a valid line to set " |
|
1644 | 1643 | "a breakpoint\n" |
|
1645 | 1644 | "after trying up to line: %s.\n" |
|
1646 | 1645 | "Please set a valid breakpoint manually " |
|
1647 | 1646 | "with the -b option." % bp) |
|
1648 | 1647 | error(msg) |
|
1649 | 1648 | return |
|
1650 | 1649 | # if we find a good linenumber, set the breakpoint |
|
1651 | 1650 | deb.do_break('%s:%s' % (filename,bp)) |
|
1652 | 1651 | # Start file run |
|
1653 | 1652 | print "NOTE: Enter 'c' at the", |
|
1654 | 1653 | print "%s prompt to start your script." % deb.prompt |
|
1655 | 1654 | try: |
|
1656 | 1655 | deb.run('execfile("%s")' % filename,prog_ns) |
|
1657 | 1656 | |
|
1658 | 1657 | except: |
|
1659 | 1658 | etype, value, tb = sys.exc_info() |
|
1660 | 1659 | # Skip three frames in the traceback: the %run one, |
|
1661 | 1660 | # one inside bdb.py, and the command-line typed by the |
|
1662 | 1661 | # user (run by exec in pdb itself). |
|
1663 | 1662 | self.shell.InteractiveTB(etype,value,tb,tb_offset=3) |
|
1664 | 1663 | else: |
|
1665 | 1664 | if runner is None: |
|
1666 | 1665 | runner = self.shell.safe_execfile |
|
1667 | 1666 | if opts.has_key('t'): |
|
1668 | 1667 | # timed execution |
|
1669 | 1668 | try: |
|
1670 | 1669 | nruns = int(opts['N'][0]) |
|
1671 | 1670 | if nruns < 1: |
|
1672 | 1671 | error('Number of runs must be >=1') |
|
1673 | 1672 | return |
|
1674 | 1673 | except (KeyError): |
|
1675 | 1674 | nruns = 1 |
|
1676 | 1675 | twall0 = time.time() |
|
1677 | 1676 | if nruns == 1: |
|
1678 | 1677 | t0 = clock2() |
|
1679 | 1678 | runner(filename,prog_ns,prog_ns, |
|
1680 | 1679 | exit_ignore=exit_ignore) |
|
1681 | 1680 | t1 = clock2() |
|
1682 | 1681 | t_usr = t1[0]-t0[0] |
|
1683 | 1682 | t_sys = t1[1]-t0[1] |
|
1684 | 1683 | print "\nIPython CPU timings (estimated):" |
|
1685 | 1684 | print " User : %10.2f s." % t_usr |
|
1686 | 1685 | print " System : %10.2f s." % t_sys |
|
1687 | 1686 | else: |
|
1688 | 1687 | runs = range(nruns) |
|
1689 | 1688 | t0 = clock2() |
|
1690 | 1689 | for nr in runs: |
|
1691 | 1690 | runner(filename,prog_ns,prog_ns, |
|
1692 | 1691 | exit_ignore=exit_ignore) |
|
1693 | 1692 | t1 = clock2() |
|
1694 | 1693 | t_usr = t1[0]-t0[0] |
|
1695 | 1694 | t_sys = t1[1]-t0[1] |
|
1696 | 1695 | print "\nIPython CPU timings (estimated):" |
|
1697 | 1696 | print "Total runs performed:",nruns |
|
1698 | 1697 | print " Times : %10.2f %10.2f" % ('Total','Per run') |
|
1699 | 1698 | print " User : %10.2f s, %10.2f s." % (t_usr,t_usr/nruns) |
|
1700 | 1699 | print " System : %10.2f s, %10.2f s." % (t_sys,t_sys/nruns) |
|
1701 | 1700 | twall1 = time.time() |
|
1702 | 1701 | print "Wall time: %10.2f s." % (twall1-twall0) |
|
1703 | 1702 | |
|
1704 | 1703 | else: |
|
1705 | 1704 | # regular execution |
|
1706 | 1705 | runner(filename,prog_ns,prog_ns,exit_ignore=exit_ignore) |
|
1707 | 1706 | |
|
1708 | 1707 | if opts.has_key('i'): |
|
1709 | 1708 | self.shell.user_ns['__name__'] = __name__save |
|
1710 | 1709 | else: |
|
1711 | 1710 | # The shell MUST hold a reference to prog_ns so after %run |
|
1712 | 1711 | # exits, the python deletion mechanism doesn't zero it out |
|
1713 | 1712 | # (leaving dangling references). |
|
1714 | 1713 | self.shell.cache_main_mod(prog_ns,filename) |
|
1715 | 1714 | # update IPython interactive namespace |
|
1716 | 1715 | |
|
1717 | 1716 | # Some forms of read errors on the file may mean the |
|
1718 | 1717 | # __name__ key was never set; using pop we don't have to |
|
1719 | 1718 | # worry about a possible KeyError. |
|
1720 | 1719 | prog_ns.pop('__name__', None) |
|
1721 | 1720 | |
|
1722 | 1721 | self.shell.user_ns.update(prog_ns) |
|
1723 | 1722 | finally: |
|
1724 | 1723 | # It's a bit of a mystery why, but __builtins__ can change from |
|
1725 | 1724 | # being a module to becoming a dict missing some key data after |
|
1726 | 1725 | # %run. As best I can see, this is NOT something IPython is doing |
|
1727 | 1726 | # at all, and similar problems have been reported before: |
|
1728 | 1727 | # http://coding.derkeiler.com/Archive/Python/comp.lang.python/2004-10/0188.html |
|
1729 | 1728 | # Since this seems to be done by the interpreter itself, the best |
|
1730 | 1729 | # we can do is to at least restore __builtins__ for the user on |
|
1731 | 1730 | # exit. |
|
1732 | 1731 | self.shell.user_ns['__builtins__'] = __builtin__ |
|
1733 | 1732 | |
|
1734 | 1733 | # Ensure key global structures are restored |
|
1735 | 1734 | sys.argv = save_argv |
|
1736 | 1735 | if restore_main: |
|
1737 | 1736 | sys.modules['__main__'] = restore_main |
|
1738 | 1737 | else: |
|
1739 | 1738 | # Remove from sys.modules the reference to main_mod we'd |
|
1740 | 1739 | # added. Otherwise it will trap references to objects |
|
1741 | 1740 | # contained therein. |
|
1742 | 1741 | del sys.modules[main_mod_name] |
|
1743 | 1742 | |
|
1744 | 1743 | return stats |
|
1745 | 1744 | |
|
1746 | 1745 | @skip_doctest |
|
1747 | 1746 | def magic_timeit(self, parameter_s =''): |
|
1748 | 1747 | """Time execution of a Python statement or expression |
|
1749 | 1748 | |
|
1750 | 1749 | Usage:\\ |
|
1751 | 1750 | %timeit [-n<N> -r<R> [-t|-c]] statement |
|
1752 | 1751 | |
|
1753 | 1752 | Time execution of a Python statement or expression using the timeit |
|
1754 | 1753 | module. |
|
1755 | 1754 | |
|
1756 | 1755 | Options: |
|
1757 | 1756 | -n<N>: execute the given statement <N> times in a loop. If this value |
|
1758 | 1757 | is not given, a fitting value is chosen. |
|
1759 | 1758 | |
|
1760 | 1759 | -r<R>: repeat the loop iteration <R> times and take the best result. |
|
1761 | 1760 | Default: 3 |
|
1762 | 1761 | |
|
1763 | 1762 | -t: use time.time to measure the time, which is the default on Unix. |
|
1764 | 1763 | This function measures wall time. |
|
1765 | 1764 | |
|
1766 | 1765 | -c: use time.clock to measure the time, which is the default on |
|
1767 | 1766 | Windows and measures wall time. On Unix, resource.getrusage is used |
|
1768 | 1767 | instead and returns the CPU user time. |
|
1769 | 1768 | |
|
1770 | 1769 | -p<P>: use a precision of <P> digits to display the timing result. |
|
1771 | 1770 | Default: 3 |
|
1772 | 1771 | |
|
1773 | 1772 | |
|
1774 | 1773 | Examples: |
|
1775 | 1774 | |
|
1776 | 1775 | In [1]: %timeit pass |
|
1777 | 1776 | 10000000 loops, best of 3: 53.3 ns per loop |
|
1778 | 1777 | |
|
1779 | 1778 | In [2]: u = None |
|
1780 | 1779 | |
|
1781 | 1780 | In [3]: %timeit u is None |
|
1782 | 1781 | 10000000 loops, best of 3: 184 ns per loop |
|
1783 | 1782 | |
|
1784 | 1783 | In [4]: %timeit -r 4 u == None |
|
1785 | 1784 | 1000000 loops, best of 4: 242 ns per loop |
|
1786 | 1785 | |
|
1787 | 1786 | In [5]: import time |
|
1788 | 1787 | |
|
1789 | 1788 | In [6]: %timeit -n1 time.sleep(2) |
|
1790 | 1789 | 1 loops, best of 3: 2 s per loop |
|
1791 | 1790 | |
|
1792 | 1791 | |
|
1793 | 1792 | The times reported by %timeit will be slightly higher than those |
|
1794 | 1793 | reported by the timeit.py script when variables are accessed. This is |
|
1795 | 1794 | due to the fact that %timeit executes the statement in the namespace |
|
1796 | 1795 | of the shell, compared with timeit.py, which uses a single setup |
|
1797 | 1796 | statement to import function or create variables. Generally, the bias |
|
1798 | 1797 | does not matter as long as results from timeit.py are not mixed with |
|
1799 | 1798 | those from %timeit.""" |
|
1800 | 1799 | |
|
1801 | 1800 | import timeit |
|
1802 | 1801 | import math |
|
1803 | 1802 | |
|
1804 | 1803 | # XXX: Unfortunately the unicode 'micro' symbol can cause problems in |
|
1805 | 1804 | # certain terminals. Until we figure out a robust way of |
|
1806 | 1805 | # auto-detecting if the terminal can deal with it, use plain 'us' for |
|
1807 | 1806 | # microseconds. I am really NOT happy about disabling the proper |
|
1808 | 1807 | # 'micro' prefix, but crashing is worse... If anyone knows what the |
|
1809 | 1808 | # right solution for this is, I'm all ears... |
|
1810 | 1809 | # |
|
1811 | 1810 | # Note: using |
|
1812 | 1811 | # |
|
1813 | 1812 | # s = u'\xb5' |
|
1814 | 1813 | # s.encode(sys.getdefaultencoding()) |
|
1815 | 1814 | # |
|
1816 | 1815 | # is not sufficient, as I've seen terminals where that fails but |
|
1817 | 1816 | # print s |
|
1818 | 1817 | # |
|
1819 | 1818 | # succeeds |
|
1820 | 1819 | # |
|
1821 | 1820 | # See bug: https://bugs.launchpad.net/ipython/+bug/348466 |
|
1822 | 1821 | |
|
1823 | 1822 | #units = [u"s", u"ms",u'\xb5',"ns"] |
|
1824 | 1823 | units = [u"s", u"ms",u'us',"ns"] |
|
1825 | 1824 | |
|
1826 | 1825 | scaling = [1, 1e3, 1e6, 1e9] |
|
1827 | 1826 | |
|
1828 | 1827 | opts, stmt = self.parse_options(parameter_s,'n:r:tcp:', |
|
1829 | 1828 | posix=False) |
|
1830 | 1829 | if stmt == "": |
|
1831 | 1830 | return |
|
1832 | 1831 | timefunc = timeit.default_timer |
|
1833 | 1832 | number = int(getattr(opts, "n", 0)) |
|
1834 | 1833 | repeat = int(getattr(opts, "r", timeit.default_repeat)) |
|
1835 | 1834 | precision = int(getattr(opts, "p", 3)) |
|
1836 | 1835 | if hasattr(opts, "t"): |
|
1837 | 1836 | timefunc = time.time |
|
1838 | 1837 | if hasattr(opts, "c"): |
|
1839 | 1838 | timefunc = clock |
|
1840 | 1839 | |
|
1841 | 1840 | timer = timeit.Timer(timer=timefunc) |
|
1842 | 1841 | # this code has tight coupling to the inner workings of timeit.Timer, |
|
1843 | 1842 | # but is there a better way to achieve that the code stmt has access |
|
1844 | 1843 | # to the shell namespace? |
|
1845 | 1844 | |
|
1846 | 1845 | src = timeit.template % {'stmt': timeit.reindent(stmt, 8), |
|
1847 | 1846 | 'setup': "pass"} |
|
1848 | 1847 | # Track compilation time so it can be reported if too long |
|
1849 | 1848 | # Minimum time above which compilation time will be reported |
|
1850 | 1849 | tc_min = 0.1 |
|
1851 | 1850 | |
|
1852 | 1851 | t0 = clock() |
|
1853 | 1852 | code = compile(src, "<magic-timeit>", "exec") |
|
1854 | 1853 | tc = clock()-t0 |
|
1855 | 1854 | |
|
1856 | 1855 | ns = {} |
|
1857 | 1856 | exec code in self.shell.user_ns, ns |
|
1858 | 1857 | timer.inner = ns["inner"] |
|
1859 | 1858 | |
|
1860 | 1859 | if number == 0: |
|
1861 | 1860 | # determine number so that 0.2 <= total time < 2.0 |
|
1862 | 1861 | number = 1 |
|
1863 | 1862 | for i in range(1, 10): |
|
1864 | 1863 | if timer.timeit(number) >= 0.2: |
|
1865 | 1864 | break |
|
1866 | 1865 | number *= 10 |
|
1867 | 1866 | |
|
1868 | 1867 | best = min(timer.repeat(repeat, number)) / number |
|
1869 | 1868 | |
|
1870 | 1869 | if best > 0.0 and best < 1000.0: |
|
1871 | 1870 | order = min(-int(math.floor(math.log10(best)) // 3), 3) |
|
1872 | 1871 | elif best >= 1000.0: |
|
1873 | 1872 | order = 0 |
|
1874 | 1873 | else: |
|
1875 | 1874 | order = 3 |
|
1876 | 1875 | print u"%d loops, best of %d: %.*g %s per loop" % (number, repeat, |
|
1877 | 1876 | precision, |
|
1878 | 1877 | best * scaling[order], |
|
1879 | 1878 | units[order]) |
|
1880 | 1879 | if tc > tc_min: |
|
1881 | 1880 | print "Compiler time: %.2f s" % tc |
|
1882 | 1881 | |
|
1883 | 1882 | @skip_doctest |
|
1884 | 1883 | @needs_local_scope |
|
1885 | 1884 | def magic_time(self,parameter_s = ''): |
|
1886 | 1885 | """Time execution of a Python statement or expression. |
|
1887 | 1886 | |
|
1888 | 1887 | The CPU and wall clock times are printed, and the value of the |
|
1889 | 1888 | expression (if any) is returned. Note that under Win32, system time |
|
1890 | 1889 | is always reported as 0, since it can not be measured. |
|
1891 | 1890 | |
|
1892 | 1891 | This function provides very basic timing functionality. In Python |
|
1893 | 1892 | 2.3, the timeit module offers more control and sophistication, so this |
|
1894 | 1893 | could be rewritten to use it (patches welcome). |
|
1895 | 1894 | |
|
1896 | 1895 | Some examples: |
|
1897 | 1896 | |
|
1898 | 1897 | In [1]: time 2**128 |
|
1899 | 1898 | CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s |
|
1900 | 1899 | Wall time: 0.00 |
|
1901 | 1900 | Out[1]: 340282366920938463463374607431768211456L |
|
1902 | 1901 | |
|
1903 | 1902 | In [2]: n = 1000000 |
|
1904 | 1903 | |
|
1905 | 1904 | In [3]: time sum(range(n)) |
|
1906 | 1905 | CPU times: user 1.20 s, sys: 0.05 s, total: 1.25 s |
|
1907 | 1906 | Wall time: 1.37 |
|
1908 | 1907 | Out[3]: 499999500000L |
|
1909 | 1908 | |
|
1910 | 1909 | In [4]: time print 'hello world' |
|
1911 | 1910 | hello world |
|
1912 | 1911 | CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s |
|
1913 | 1912 | Wall time: 0.00 |
|
1914 | 1913 | |
|
1915 | 1914 | Note that the time needed by Python to compile the given expression |
|
1916 | 1915 | will be reported if it is more than 0.1s. In this example, the |
|
1917 | 1916 | actual exponentiation is done by Python at compilation time, so while |
|
1918 | 1917 | the expression can take a noticeable amount of time to compute, that |
|
1919 | 1918 | time is purely due to the compilation: |
|
1920 | 1919 | |
|
1921 | 1920 | In [5]: time 3**9999; |
|
1922 | 1921 | CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s |
|
1923 | 1922 | Wall time: 0.00 s |
|
1924 | 1923 | |
|
1925 | 1924 | In [6]: time 3**999999; |
|
1926 | 1925 | CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s |
|
1927 | 1926 | Wall time: 0.00 s |
|
1928 | 1927 | Compiler : 0.78 s |
|
1929 | 1928 | """ |
|
1930 | 1929 | |
|
1931 | 1930 | # fail immediately if the given expression can't be compiled |
|
1932 | 1931 | |
|
1933 | 1932 | expr = self.shell.prefilter(parameter_s,False) |
|
1934 | 1933 | |
|
1935 | 1934 | # Minimum time above which compilation time will be reported |
|
1936 | 1935 | tc_min = 0.1 |
|
1937 | 1936 | |
|
1938 | 1937 | try: |
|
1939 | 1938 | mode = 'eval' |
|
1940 | 1939 | t0 = clock() |
|
1941 | 1940 | code = compile(expr,'<timed eval>',mode) |
|
1942 | 1941 | tc = clock()-t0 |
|
1943 | 1942 | except SyntaxError: |
|
1944 | 1943 | mode = 'exec' |
|
1945 | 1944 | t0 = clock() |
|
1946 | 1945 | code = compile(expr,'<timed exec>',mode) |
|
1947 | 1946 | tc = clock()-t0 |
|
1948 | 1947 | # skew measurement as little as possible |
|
1949 | 1948 | glob = self.shell.user_ns |
|
1950 | 1949 | locs = self._magic_locals |
|
1951 | 1950 | clk = clock2 |
|
1952 | 1951 | wtime = time.time |
|
1953 | 1952 | # time execution |
|
1954 | 1953 | wall_st = wtime() |
|
1955 | 1954 | if mode=='eval': |
|
1956 | 1955 | st = clk() |
|
1957 | 1956 | out = eval(code, glob, locs) |
|
1958 | 1957 | end = clk() |
|
1959 | 1958 | else: |
|
1960 | 1959 | st = clk() |
|
1961 | 1960 | exec code in glob, locs |
|
1962 | 1961 | end = clk() |
|
1963 | 1962 | out = None |
|
1964 | 1963 | wall_end = wtime() |
|
1965 | 1964 | # Compute actual times and report |
|
1966 | 1965 | wall_time = wall_end-wall_st |
|
1967 | 1966 | cpu_user = end[0]-st[0] |
|
1968 | 1967 | cpu_sys = end[1]-st[1] |
|
1969 | 1968 | cpu_tot = cpu_user+cpu_sys |
|
1970 | 1969 | print "CPU times: user %.2f s, sys: %.2f s, total: %.2f s" % \ |
|
1971 | 1970 | (cpu_user,cpu_sys,cpu_tot) |
|
1972 | 1971 | print "Wall time: %.2f s" % wall_time |
|
1973 | 1972 | if tc > tc_min: |
|
1974 | 1973 | print "Compiler : %.2f s" % tc |
|
1975 | 1974 | return out |
|
1976 | 1975 | |
|
1977 | 1976 | @skip_doctest |
|
1978 | 1977 | def magic_macro(self,parameter_s = ''): |
|
1979 | 1978 | """Define a macro for future re-execution. It accepts ranges of history, |
|
1980 | 1979 | filenames or string objects. |
|
1981 | 1980 | |
|
1982 | 1981 | Usage:\\ |
|
1983 | 1982 | %macro [options] name n1-n2 n3-n4 ... n5 .. n6 ... |
|
1984 | 1983 | |
|
1985 | 1984 | Options: |
|
1986 | 1985 | |
|
1987 | 1986 | -r: use 'raw' input. By default, the 'processed' history is used, |
|
1988 | 1987 | so that magics are loaded in their transformed version to valid |
|
1989 | 1988 | Python. If this option is given, the raw input as typed as the |
|
1990 | 1989 | command line is used instead. |
|
1991 | 1990 | |
|
1992 | 1991 | This will define a global variable called `name` which is a string |
|
1993 | 1992 | made of joining the slices and lines you specify (n1,n2,... numbers |
|
1994 | 1993 | above) from your input history into a single string. This variable |
|
1995 | 1994 | acts like an automatic function which re-executes those lines as if |
|
1996 | 1995 | you had typed them. You just type 'name' at the prompt and the code |
|
1997 | 1996 | executes. |
|
1998 | 1997 | |
|
1999 | 1998 | The syntax for indicating input ranges is described in %history. |
|
2000 | 1999 | |
|
2001 | 2000 | Note: as a 'hidden' feature, you can also use traditional python slice |
|
2002 | 2001 | notation, where N:M means numbers N through M-1. |
|
2003 | 2002 | |
|
2004 | 2003 | For example, if your history contains (%hist prints it): |
|
2005 | 2004 | |
|
2006 | 2005 | 44: x=1 |
|
2007 | 2006 | 45: y=3 |
|
2008 | 2007 | 46: z=x+y |
|
2009 | 2008 | 47: print x |
|
2010 | 2009 | 48: a=5 |
|
2011 | 2010 | 49: print 'x',x,'y',y |
|
2012 | 2011 | |
|
2013 | 2012 | you can create a macro with lines 44 through 47 (included) and line 49 |
|
2014 | 2013 | called my_macro with: |
|
2015 | 2014 | |
|
2016 | 2015 | In [55]: %macro my_macro 44-47 49 |
|
2017 | 2016 | |
|
2018 | 2017 | Now, typing `my_macro` (without quotes) will re-execute all this code |
|
2019 | 2018 | in one pass. |
|
2020 | 2019 | |
|
2021 | 2020 | You don't need to give the line-numbers in order, and any given line |
|
2022 | 2021 | number can appear multiple times. You can assemble macros with any |
|
2023 | 2022 | lines from your input history in any order. |
|
2024 | 2023 | |
|
2025 | 2024 | The macro is a simple object which holds its value in an attribute, |
|
2026 | 2025 | but IPython's display system checks for macros and executes them as |
|
2027 | 2026 | code instead of printing them when you type their name. |
|
2028 | 2027 | |
|
2029 | 2028 | You can view a macro's contents by explicitly printing it with: |
|
2030 | 2029 | |
|
2031 | 2030 | 'print macro_name'. |
|
2032 | 2031 | |
|
2033 | 2032 | """ |
|
2034 | 2033 | opts,args = self.parse_options(parameter_s,'r',mode='list') |
|
2035 | 2034 | if not args: # List existing macros |
|
2036 | 2035 | return sorted(k for k,v in self.shell.user_ns.iteritems() if\ |
|
2037 | 2036 | isinstance(v, Macro)) |
|
2038 | 2037 | if len(args) == 1: |
|
2039 | 2038 | raise UsageError( |
|
2040 | 2039 | "%macro insufficient args; usage '%macro name n1-n2 n3-4...") |
|
2041 | 2040 | name, codefrom = args[0], " ".join(args[1:]) |
|
2042 | 2041 | |
|
2043 | 2042 | #print 'rng',ranges # dbg |
|
2044 | 2043 | try: |
|
2045 | 2044 | lines = self.shell.find_user_code(codefrom, 'r' in opts) |
|
2046 | 2045 | except (ValueError, TypeError) as e: |
|
2047 | 2046 | print e.args[0] |
|
2048 | 2047 | return |
|
2049 | 2048 | macro = Macro(lines) |
|
2050 | 2049 | self.shell.define_macro(name, macro) |
|
2051 | 2050 | print 'Macro `%s` created. To execute, type its name (without quotes).' % name |
|
2052 | 2051 | print '=== Macro contents: ===' |
|
2053 | 2052 | print macro, |
|
2054 | 2053 | |
|
2055 | 2054 | def magic_save(self,parameter_s = ''): |
|
2056 | 2055 | """Save a set of lines or a macro to a given filename. |
|
2057 | 2056 | |
|
2058 | 2057 | Usage:\\ |
|
2059 | 2058 | %save [options] filename n1-n2 n3-n4 ... n5 .. n6 ... |
|
2060 | 2059 | |
|
2061 | 2060 | Options: |
|
2062 | 2061 | |
|
2063 | 2062 | -r: use 'raw' input. By default, the 'processed' history is used, |
|
2064 | 2063 | so that magics are loaded in their transformed version to valid |
|
2065 | 2064 | Python. If this option is given, the raw input as typed as the |
|
2066 | 2065 | command line is used instead. |
|
2067 | 2066 | |
|
2068 | 2067 | This function uses the same syntax as %history for input ranges, |
|
2069 | 2068 | then saves the lines to the filename you specify. |
|
2070 | 2069 | |
|
2071 | 2070 | It adds a '.py' extension to the file if you don't do so yourself, and |
|
2072 | 2071 | it asks for confirmation before overwriting existing files.""" |
|
2073 | 2072 | |
|
2074 | 2073 | opts,args = self.parse_options(parameter_s,'r',mode='list') |
|
2075 | 2074 | fname, codefrom = args[0], " ".join(args[1:]) |
|
2076 | 2075 | if not fname.endswith('.py'): |
|
2077 | 2076 | fname += '.py' |
|
2078 | 2077 | if os.path.isfile(fname): |
|
2079 | 2078 | ans = raw_input('File `%s` exists. Overwrite (y/[N])? ' % fname) |
|
2080 | 2079 | if ans.lower() not in ['y','yes']: |
|
2081 | 2080 | print 'Operation cancelled.' |
|
2082 | 2081 | return |
|
2083 | 2082 | try: |
|
2084 | 2083 | cmds = self.shell.find_user_code(codefrom, 'r' in opts) |
|
2085 | 2084 | except (TypeError, ValueError) as e: |
|
2086 | 2085 | print e.args[0] |
|
2087 | 2086 | return |
|
2088 | 2087 | if isinstance(cmds, unicode): |
|
2089 | 2088 | cmds = cmds.encode("utf-8") |
|
2090 | 2089 | with open(fname,'w') as f: |
|
2091 | 2090 | f.write("# coding: utf-8\n") |
|
2092 | 2091 | f.write(cmds) |
|
2093 | 2092 | print 'The following commands were written to file `%s`:' % fname |
|
2094 | 2093 | print cmds |
|
2095 | 2094 | |
|
2096 | 2095 | def magic_pastebin(self, parameter_s = ''): |
|
2097 | 2096 | """Upload code to the 'Lodge it' paste bin, returning the URL.""" |
|
2098 | 2097 | try: |
|
2099 | 2098 | code = self.shell.find_user_code(parameter_s) |
|
2100 | 2099 | except (ValueError, TypeError) as e: |
|
2101 | 2100 | print e.args[0] |
|
2102 | 2101 | return |
|
2103 | 2102 | pbserver = ServerProxy('http://paste.pocoo.org/xmlrpc/') |
|
2104 | 2103 | id = pbserver.pastes.newPaste("python", code) |
|
2105 | 2104 | return "http://paste.pocoo.org/show/" + id |
|
2106 | 2105 | |
|
2107 | 2106 | def magic_loadpy(self, arg_s): |
|
2108 | 2107 | """Load a .py python script into the GUI console. |
|
2109 | 2108 | |
|
2110 | 2109 | This magic command can either take a local filename or a url:: |
|
2111 | 2110 | |
|
2112 | 2111 | %loadpy myscript.py |
|
2113 | 2112 | %loadpy http://www.example.com/myscript.py |
|
2114 | 2113 | """ |
|
2115 | 2114 | if not arg_s.endswith('.py'): |
|
2116 | 2115 | raise ValueError('%%load only works with .py files: %s' % arg_s) |
|
2117 | 2116 | if arg_s.startswith('http'): |
|
2118 | 2117 | import urllib2 |
|
2119 | 2118 | response = urllib2.urlopen(arg_s) |
|
2120 | 2119 | content = response.read() |
|
2121 | 2120 | else: |
|
2122 | 2121 | with open(arg_s) as f: |
|
2123 | 2122 | content = f.read() |
|
2124 | 2123 | self.set_next_input(content) |
|
2125 | 2124 | |
|
2126 | 2125 | def _find_edit_target(self, args, opts, last_call): |
|
2127 | 2126 | """Utility method used by magic_edit to find what to edit.""" |
|
2128 | 2127 | |
|
2129 | 2128 | def make_filename(arg): |
|
2130 | 2129 | "Make a filename from the given args" |
|
2131 | 2130 | try: |
|
2132 | 2131 | filename = get_py_filename(arg) |
|
2133 | 2132 | except IOError: |
|
2134 | 2133 | # If it ends with .py but doesn't already exist, assume we want |
|
2135 | 2134 | # a new file. |
|
2136 | 2135 | if args.endswith('.py'): |
|
2137 | 2136 | filename = arg |
|
2138 | 2137 | else: |
|
2139 | 2138 | filename = None |
|
2140 | 2139 | return filename |
|
2141 | 2140 | |
|
2142 | 2141 | # Set a few locals from the options for convenience: |
|
2143 | 2142 | opts_prev = 'p' in opts |
|
2144 | 2143 | opts_raw = 'r' in opts |
|
2145 | 2144 | |
|
2146 | 2145 | # custom exceptions |
|
2147 | 2146 | class DataIsObject(Exception): pass |
|
2148 | 2147 | |
|
2149 | 2148 | # Default line number value |
|
2150 | 2149 | lineno = opts.get('n',None) |
|
2151 | 2150 | |
|
2152 | 2151 | if opts_prev: |
|
2153 | 2152 | args = '_%s' % last_call[0] |
|
2154 | 2153 | if not self.shell.user_ns.has_key(args): |
|
2155 | 2154 | args = last_call[1] |
|
2156 | 2155 | |
|
2157 | 2156 | # use last_call to remember the state of the previous call, but don't |
|
2158 | 2157 | # let it be clobbered by successive '-p' calls. |
|
2159 | 2158 | try: |
|
2160 | 2159 | last_call[0] = self.shell.displayhook.prompt_count |
|
2161 | 2160 | if not opts_prev: |
|
2162 | 2161 | last_call[1] = parameter_s |
|
2163 | 2162 | except: |
|
2164 | 2163 | pass |
|
2165 | 2164 | |
|
2166 | 2165 | # by default this is done with temp files, except when the given |
|
2167 | 2166 | # arg is a filename |
|
2168 | 2167 | use_temp = True |
|
2169 | 2168 | |
|
2170 | 2169 | data = '' |
|
2171 | 2170 | |
|
2172 | 2171 | # First, see if the arguments should be a filename. |
|
2173 | 2172 | filename = make_filename(args) |
|
2174 | 2173 | if filename: |
|
2175 | 2174 | use_temp = False |
|
2176 | 2175 | elif args: |
|
2177 | 2176 | # Mode where user specifies ranges of lines, like in %macro. |
|
2178 | 2177 | data = self.extract_input_lines(args, opts_raw) |
|
2179 | 2178 | if not data: |
|
2180 | 2179 | try: |
|
2181 | 2180 | # Load the parameter given as a variable. If not a string, |
|
2182 | 2181 | # process it as an object instead (below) |
|
2183 | 2182 | |
|
2184 | 2183 | #print '*** args',args,'type',type(args) # dbg |
|
2185 | 2184 | data = eval(args, self.shell.user_ns) |
|
2186 | 2185 | if not isinstance(data, basestring): |
|
2187 | 2186 | raise DataIsObject |
|
2188 | 2187 | |
|
2189 | 2188 | except (NameError,SyntaxError): |
|
2190 | 2189 | # given argument is not a variable, try as a filename |
|
2191 | 2190 | filename = make_filename(args) |
|
2192 | 2191 | if filename is None: |
|
2193 | 2192 | warn("Argument given (%s) can't be found as a variable " |
|
2194 | 2193 | "or as a filename." % args) |
|
2195 | 2194 | return |
|
2196 | 2195 | use_temp = False |
|
2197 | 2196 | |
|
2198 | 2197 | except DataIsObject: |
|
2199 | 2198 | # macros have a special edit function |
|
2200 | 2199 | if isinstance(data, Macro): |
|
2201 | 2200 | raise MacroToEdit(data) |
|
2202 | 2201 | |
|
2203 | 2202 | # For objects, try to edit the file where they are defined |
|
2204 | 2203 | try: |
|
2205 | 2204 | filename = inspect.getabsfile(data) |
|
2206 | 2205 | if 'fakemodule' in filename.lower() and inspect.isclass(data): |
|
2207 | 2206 | # class created by %edit? Try to find source |
|
2208 | 2207 | # by looking for method definitions instead, the |
|
2209 | 2208 | # __module__ in those classes is FakeModule. |
|
2210 | 2209 | attrs = [getattr(data, aname) for aname in dir(data)] |
|
2211 | 2210 | for attr in attrs: |
|
2212 | 2211 | if not inspect.ismethod(attr): |
|
2213 | 2212 | continue |
|
2214 | 2213 | filename = inspect.getabsfile(attr) |
|
2215 | 2214 | if filename and 'fakemodule' not in filename.lower(): |
|
2216 | 2215 | # change the attribute to be the edit target instead |
|
2217 | 2216 | data = attr |
|
2218 | 2217 | break |
|
2219 | 2218 | |
|
2220 | 2219 | datafile = 1 |
|
2221 | 2220 | except TypeError: |
|
2222 | 2221 | filename = make_filename(args) |
|
2223 | 2222 | datafile = 1 |
|
2224 | 2223 | warn('Could not find file where `%s` is defined.\n' |
|
2225 | 2224 | 'Opening a file named `%s`' % (args,filename)) |
|
2226 | 2225 | # Now, make sure we can actually read the source (if it was in |
|
2227 | 2226 | # a temp file it's gone by now). |
|
2228 | 2227 | if datafile: |
|
2229 | 2228 | try: |
|
2230 | 2229 | if lineno is None: |
|
2231 | 2230 | lineno = inspect.getsourcelines(data)[1] |
|
2232 | 2231 | except IOError: |
|
2233 | 2232 | filename = make_filename(args) |
|
2234 | 2233 | if filename is None: |
|
2235 | 2234 | warn('The file `%s` where `%s` was defined cannot ' |
|
2236 | 2235 | 'be read.' % (filename,data)) |
|
2237 | 2236 | return |
|
2238 | 2237 | use_temp = False |
|
2239 | 2238 | |
|
2240 | 2239 | if use_temp: |
|
2241 | 2240 | filename = self.shell.mktempfile(data) |
|
2242 | 2241 | print 'IPython will make a temporary file named:',filename |
|
2243 | 2242 | |
|
2244 | 2243 | return filename, lineno, use_temp |
|
2245 | 2244 | |
|
2246 | 2245 | def _edit_macro(self,mname,macro): |
|
2247 | 2246 | """open an editor with the macro data in a file""" |
|
2248 | 2247 | filename = self.shell.mktempfile(macro.value) |
|
2249 | 2248 | self.shell.hooks.editor(filename) |
|
2250 | 2249 | |
|
2251 | 2250 | # and make a new macro object, to replace the old one |
|
2252 | 2251 | mfile = open(filename) |
|
2253 | 2252 | mvalue = mfile.read() |
|
2254 | 2253 | mfile.close() |
|
2255 | 2254 | self.shell.user_ns[mname] = Macro(mvalue) |
|
2256 | 2255 | |
|
2257 | 2256 | def magic_ed(self,parameter_s=''): |
|
2258 | 2257 | """Alias to %edit.""" |
|
2259 | 2258 | return self.magic_edit(parameter_s) |
|
2260 | 2259 | |
|
2261 | 2260 | @skip_doctest |
|
2262 | 2261 | def magic_edit(self,parameter_s='',last_call=['','']): |
|
2263 | 2262 | """Bring up an editor and execute the resulting code. |
|
2264 | 2263 | |
|
2265 | 2264 | Usage: |
|
2266 | 2265 | %edit [options] [args] |
|
2267 | 2266 | |
|
2268 | 2267 | %edit runs IPython's editor hook. The default version of this hook is |
|
2269 | 2268 | set to call the __IPYTHON__.rc.editor command. This is read from your |
|
2270 | 2269 | environment variable $EDITOR. If this isn't found, it will default to |
|
2271 | 2270 | vi under Linux/Unix and to notepad under Windows. See the end of this |
|
2272 | 2271 | docstring for how to change the editor hook. |
|
2273 | 2272 | |
|
2274 | 2273 | You can also set the value of this editor via the command line option |
|
2275 | 2274 | '-editor' or in your ipythonrc file. This is useful if you wish to use |
|
2276 | 2275 | specifically for IPython an editor different from your typical default |
|
2277 | 2276 | (and for Windows users who typically don't set environment variables). |
|
2278 | 2277 | |
|
2279 | 2278 | This command allows you to conveniently edit multi-line code right in |
|
2280 | 2279 | your IPython session. |
|
2281 | 2280 | |
|
2282 | 2281 | If called without arguments, %edit opens up an empty editor with a |
|
2283 | 2282 | temporary file and will execute the contents of this file when you |
|
2284 | 2283 | close it (don't forget to save it!). |
|
2285 | 2284 | |
|
2286 | 2285 | |
|
2287 | 2286 | Options: |
|
2288 | 2287 | |
|
2289 | 2288 | -n <number>: open the editor at a specified line number. By default, |
|
2290 | 2289 | the IPython editor hook uses the unix syntax 'editor +N filename', but |
|
2291 | 2290 | you can configure this by providing your own modified hook if your |
|
2292 | 2291 | favorite editor supports line-number specifications with a different |
|
2293 | 2292 | syntax. |
|
2294 | 2293 | |
|
2295 | 2294 | -p: this will call the editor with the same data as the previous time |
|
2296 | 2295 | it was used, regardless of how long ago (in your current session) it |
|
2297 | 2296 | was. |
|
2298 | 2297 | |
|
2299 | 2298 | -r: use 'raw' input. This option only applies to input taken from the |
|
2300 | 2299 | user's history. By default, the 'processed' history is used, so that |
|
2301 | 2300 | magics are loaded in their transformed version to valid Python. If |
|
2302 | 2301 | this option is given, the raw input as typed as the command line is |
|
2303 | 2302 | used instead. When you exit the editor, it will be executed by |
|
2304 | 2303 | IPython's own processor. |
|
2305 | 2304 | |
|
2306 | 2305 | -x: do not execute the edited code immediately upon exit. This is |
|
2307 | 2306 | mainly useful if you are editing programs which need to be called with |
|
2308 | 2307 | command line arguments, which you can then do using %run. |
|
2309 | 2308 | |
|
2310 | 2309 | |
|
2311 | 2310 | Arguments: |
|
2312 | 2311 | |
|
2313 | 2312 | If arguments are given, the following possibilites exist: |
|
2314 | 2313 | |
|
2315 | 2314 | - If the argument is a filename, IPython will load that into the |
|
2316 | 2315 | editor. It will execute its contents with execfile() when you exit, |
|
2317 | 2316 | loading any code in the file into your interactive namespace. |
|
2318 | 2317 | |
|
2319 | 2318 | - The arguments are ranges of input history, e.g. "7 ~1/4-6". |
|
2320 | 2319 | The syntax is the same as in the %history magic. |
|
2321 | 2320 | |
|
2322 | 2321 | - If the argument is a string variable, its contents are loaded |
|
2323 | 2322 | into the editor. You can thus edit any string which contains |
|
2324 | 2323 | python code (including the result of previous edits). |
|
2325 | 2324 | |
|
2326 | 2325 | - If the argument is the name of an object (other than a string), |
|
2327 | 2326 | IPython will try to locate the file where it was defined and open the |
|
2328 | 2327 | editor at the point where it is defined. You can use `%edit function` |
|
2329 | 2328 | to load an editor exactly at the point where 'function' is defined, |
|
2330 | 2329 | edit it and have the file be executed automatically. |
|
2331 | 2330 | |
|
2332 | 2331 | If the object is a macro (see %macro for details), this opens up your |
|
2333 | 2332 | specified editor with a temporary file containing the macro's data. |
|
2334 | 2333 | Upon exit, the macro is reloaded with the contents of the file. |
|
2335 | 2334 | |
|
2336 | 2335 | Note: opening at an exact line is only supported under Unix, and some |
|
2337 | 2336 | editors (like kedit and gedit up to Gnome 2.8) do not understand the |
|
2338 | 2337 | '+NUMBER' parameter necessary for this feature. Good editors like |
|
2339 | 2338 | (X)Emacs, vi, jed, pico and joe all do. |
|
2340 | 2339 | |
|
2341 | 2340 | After executing your code, %edit will return as output the code you |
|
2342 | 2341 | typed in the editor (except when it was an existing file). This way |
|
2343 | 2342 | you can reload the code in further invocations of %edit as a variable, |
|
2344 | 2343 | via _<NUMBER> or Out[<NUMBER>], where <NUMBER> is the prompt number of |
|
2345 | 2344 | the output. |
|
2346 | 2345 | |
|
2347 | 2346 | Note that %edit is also available through the alias %ed. |
|
2348 | 2347 | |
|
2349 | 2348 | This is an example of creating a simple function inside the editor and |
|
2350 | 2349 | then modifying it. First, start up the editor: |
|
2351 | 2350 | |
|
2352 | 2351 | In [1]: ed |
|
2353 | 2352 | Editing... done. Executing edited code... |
|
2354 | 2353 | Out[1]: 'def foo():n print "foo() was defined in an editing session"n' |
|
2355 | 2354 | |
|
2356 | 2355 | We can then call the function foo(): |
|
2357 | 2356 | |
|
2358 | 2357 | In [2]: foo() |
|
2359 | 2358 | foo() was defined in an editing session |
|
2360 | 2359 | |
|
2361 | 2360 | Now we edit foo. IPython automatically loads the editor with the |
|
2362 | 2361 | (temporary) file where foo() was previously defined: |
|
2363 | 2362 | |
|
2364 | 2363 | In [3]: ed foo |
|
2365 | 2364 | Editing... done. Executing edited code... |
|
2366 | 2365 | |
|
2367 | 2366 | And if we call foo() again we get the modified version: |
|
2368 | 2367 | |
|
2369 | 2368 | In [4]: foo() |
|
2370 | 2369 | foo() has now been changed! |
|
2371 | 2370 | |
|
2372 | 2371 | Here is an example of how to edit a code snippet successive |
|
2373 | 2372 | times. First we call the editor: |
|
2374 | 2373 | |
|
2375 | 2374 | In [5]: ed |
|
2376 | 2375 | Editing... done. Executing edited code... |
|
2377 | 2376 | hello |
|
2378 | 2377 | Out[5]: "print 'hello'n" |
|
2379 | 2378 | |
|
2380 | 2379 | Now we call it again with the previous output (stored in _): |
|
2381 | 2380 | |
|
2382 | 2381 | In [6]: ed _ |
|
2383 | 2382 | Editing... done. Executing edited code... |
|
2384 | 2383 | hello world |
|
2385 | 2384 | Out[6]: "print 'hello world'n" |
|
2386 | 2385 | |
|
2387 | 2386 | Now we call it with the output #8 (stored in _8, also as Out[8]): |
|
2388 | 2387 | |
|
2389 | 2388 | In [7]: ed _8 |
|
2390 | 2389 | Editing... done. Executing edited code... |
|
2391 | 2390 | hello again |
|
2392 | 2391 | Out[7]: "print 'hello again'n" |
|
2393 | 2392 | |
|
2394 | 2393 | |
|
2395 | 2394 | Changing the default editor hook: |
|
2396 | 2395 | |
|
2397 | 2396 | If you wish to write your own editor hook, you can put it in a |
|
2398 | 2397 | configuration file which you load at startup time. The default hook |
|
2399 | 2398 | is defined in the IPython.core.hooks module, and you can use that as a |
|
2400 | 2399 | starting example for further modifications. That file also has |
|
2401 | 2400 | general instructions on how to set a new hook for use once you've |
|
2402 | 2401 | defined it.""" |
|
2403 | 2402 | opts,args = self.parse_options(parameter_s,'prxn:') |
|
2404 | 2403 | |
|
2405 | 2404 | try: |
|
2406 | 2405 | filename, lineno, is_temp = self._find_edit_target(args, opts, last_call) |
|
2407 | 2406 | except MacroToEdit as e: |
|
2408 | 2407 | self._edit_macro(args, e.args[0]) |
|
2409 | 2408 | return |
|
2410 | 2409 | |
|
2411 | 2410 | # do actual editing here |
|
2412 | 2411 | print 'Editing...', |
|
2413 | 2412 | sys.stdout.flush() |
|
2414 | 2413 | try: |
|
2415 | 2414 | # Quote filenames that may have spaces in them |
|
2416 | 2415 | if ' ' in filename: |
|
2417 | 2416 | filename = "'%s'" % filename |
|
2418 | 2417 | self.shell.hooks.editor(filename,lineno) |
|
2419 | 2418 | except TryNext: |
|
2420 | 2419 | warn('Could not open editor') |
|
2421 | 2420 | return |
|
2422 | 2421 | |
|
2423 | 2422 | # XXX TODO: should this be generalized for all string vars? |
|
2424 | 2423 | # For now, this is special-cased to blocks created by cpaste |
|
2425 | 2424 | if args.strip() == 'pasted_block': |
|
2426 | 2425 | self.shell.user_ns['pasted_block'] = file_read(filename) |
|
2427 | 2426 | |
|
2428 | 2427 | if 'x' in opts: # -x prevents actual execution |
|
2429 | 2428 | |
|
2430 | 2429 | else: |
|
2431 | 2430 | print 'done. Executing edited code...' |
|
2432 | 2431 | if 'r' in opts: # Untranslated IPython code |
|
2433 | 2432 | self.shell.run_cell(file_read(filename), |
|
2434 | 2433 | store_history=False) |
|
2435 | 2434 | else: |
|
2436 | 2435 | self.shell.safe_execfile(filename,self.shell.user_ns, |
|
2437 | 2436 | self.shell.user_ns) |
|
2438 | 2437 | |
|
2439 | 2438 | if is_temp: |
|
2440 | 2439 | try: |
|
2441 | 2440 | return open(filename).read() |
|
2442 | 2441 | except IOError,msg: |
|
2443 | 2442 | if msg.filename == filename: |
|
2444 | 2443 | warn('File not found. Did you forget to save?') |
|
2445 | 2444 | return |
|
2446 | 2445 | else: |
|
2447 | 2446 | self.shell.showtraceback() |
|
2448 | 2447 | |
|
2449 | 2448 | def magic_xmode(self,parameter_s = ''): |
|
2450 | 2449 | """Switch modes for the exception handlers. |
|
2451 | 2450 | |
|
2452 | 2451 | Valid modes: Plain, Context and Verbose. |
|
2453 | 2452 | |
|
2454 | 2453 | If called without arguments, acts as a toggle.""" |
|
2455 | 2454 | |
|
2456 | 2455 | def xmode_switch_err(name): |
|
2457 | 2456 | warn('Error changing %s exception modes.\n%s' % |
|
2458 | 2457 | (name,sys.exc_info()[1])) |
|
2459 | 2458 | |
|
2460 | 2459 | shell = self.shell |
|
2461 | 2460 | new_mode = parameter_s.strip().capitalize() |
|
2462 | 2461 | try: |
|
2463 | 2462 | shell.InteractiveTB.set_mode(mode=new_mode) |
|
2464 | 2463 | print 'Exception reporting mode:',shell.InteractiveTB.mode |
|
2465 | 2464 | except: |
|
2466 | 2465 | xmode_switch_err('user') |
|
2467 | 2466 | |
|
2468 | 2467 | def magic_colors(self,parameter_s = ''): |
|
2469 | 2468 | """Switch color scheme for prompts, info system and exception handlers. |
|
2470 | 2469 | |
|
2471 | 2470 | Currently implemented schemes: NoColor, Linux, LightBG. |
|
2472 | 2471 | |
|
2473 | 2472 | Color scheme names are not case-sensitive. |
|
2474 | 2473 | |
|
2475 | 2474 | Examples |
|
2476 | 2475 | -------- |
|
2477 | 2476 | To get a plain black and white terminal:: |
|
2478 | 2477 | |
|
2479 | 2478 | %colors nocolor |
|
2480 | 2479 | """ |
|
2481 | 2480 | |
|
2482 | 2481 | def color_switch_err(name): |
|
2483 | 2482 | warn('Error changing %s color schemes.\n%s' % |
|
2484 | 2483 | (name,sys.exc_info()[1])) |
|
2485 | 2484 | |
|
2486 | 2485 | |
|
2487 | 2486 | new_scheme = parameter_s.strip() |
|
2488 | 2487 | if not new_scheme: |
|
2489 | 2488 | raise UsageError( |
|
2490 | 2489 | "%colors: you must specify a color scheme. See '%colors?'") |
|
2491 | 2490 | return |
|
2492 | 2491 | # local shortcut |
|
2493 | 2492 | shell = self.shell |
|
2494 | 2493 | |
|
2495 | 2494 | import IPython.utils.rlineimpl as readline |
|
2496 | 2495 | |
|
2497 | 2496 | if not readline.have_readline and sys.platform == "win32": |
|
2498 | 2497 | msg = """\ |
|
2499 | 2498 | Proper color support under MS Windows requires the pyreadline library. |
|
2500 | 2499 | You can find it at: |
|
2501 | 2500 | http://ipython.scipy.org/moin/PyReadline/Intro |
|
2502 | 2501 | Gary's readline needs the ctypes module, from: |
|
2503 | 2502 | http://starship.python.net/crew/theller/ctypes |
|
2504 | 2503 | (Note that ctypes is already part of Python versions 2.5 and newer). |
|
2505 | 2504 | |
|
2506 | 2505 | Defaulting color scheme to 'NoColor'""" |
|
2507 | 2506 | new_scheme = 'NoColor' |
|
2508 | 2507 | warn(msg) |
|
2509 | 2508 | |
|
2510 | 2509 | # readline option is 0 |
|
2511 | 2510 | if not shell.has_readline: |
|
2512 | 2511 | new_scheme = 'NoColor' |
|
2513 | 2512 | |
|
2514 | 2513 | # Set prompt colors |
|
2515 | 2514 | try: |
|
2516 | 2515 | shell.displayhook.set_colors(new_scheme) |
|
2517 | 2516 | except: |
|
2518 | 2517 | color_switch_err('prompt') |
|
2519 | 2518 | else: |
|
2520 | 2519 | shell.colors = \ |
|
2521 | 2520 | shell.displayhook.color_table.active_scheme_name |
|
2522 | 2521 | # Set exception colors |
|
2523 | 2522 | try: |
|
2524 | 2523 | shell.InteractiveTB.set_colors(scheme = new_scheme) |
|
2525 | 2524 | shell.SyntaxTB.set_colors(scheme = new_scheme) |
|
2526 | 2525 | except: |
|
2527 | 2526 | color_switch_err('exception') |
|
2528 | 2527 | |
|
2529 | 2528 | # Set info (for 'object?') colors |
|
2530 | 2529 | if shell.color_info: |
|
2531 | 2530 | try: |
|
2532 | 2531 | shell.inspector.set_active_scheme(new_scheme) |
|
2533 | 2532 | except: |
|
2534 | 2533 | color_switch_err('object inspector') |
|
2535 | 2534 | else: |
|
2536 | 2535 | shell.inspector.set_active_scheme('NoColor') |
|
2537 | 2536 | |
|
2538 | 2537 | def magic_pprint(self, parameter_s=''): |
|
2539 | 2538 | """Toggle pretty printing on/off.""" |
|
2540 | 2539 | ptformatter = self.shell.display_formatter.formatters['text/plain'] |
|
2541 | 2540 | ptformatter.pprint = bool(1 - ptformatter.pprint) |
|
2542 | 2541 | print 'Pretty printing has been turned', \ |
|
2543 | 2542 | ['OFF','ON'][ptformatter.pprint] |
|
2544 | 2543 | |
|
2545 | 2544 | #...................................................................... |
|
2546 | 2545 | # Functions to implement unix shell-type things |
|
2547 | 2546 | |
|
2548 | 2547 | @skip_doctest |
|
2549 | 2548 | def magic_alias(self, parameter_s = ''): |
|
2550 | 2549 | """Define an alias for a system command. |
|
2551 | 2550 | |
|
2552 | 2551 | '%alias alias_name cmd' defines 'alias_name' as an alias for 'cmd' |
|
2553 | 2552 | |
|
2554 | 2553 | Then, typing 'alias_name params' will execute the system command 'cmd |
|
2555 | 2554 | params' (from your underlying operating system). |
|
2556 | 2555 | |
|
2557 | 2556 | Aliases have lower precedence than magic functions and Python normal |
|
2558 | 2557 | variables, so if 'foo' is both a Python variable and an alias, the |
|
2559 | 2558 | alias can not be executed until 'del foo' removes the Python variable. |
|
2560 | 2559 | |
|
2561 | 2560 | You can use the %l specifier in an alias definition to represent the |
|
2562 | 2561 | whole line when the alias is called. For example: |
|
2563 | 2562 | |
|
2564 | 2563 | In [2]: alias bracket echo "Input in brackets: <%l>" |
|
2565 | 2564 | In [3]: bracket hello world |
|
2566 | 2565 | Input in brackets: <hello world> |
|
2567 | 2566 | |
|
2568 | 2567 | You can also define aliases with parameters using %s specifiers (one |
|
2569 | 2568 | per parameter): |
|
2570 | 2569 | |
|
2571 | 2570 | In [1]: alias parts echo first %s second %s |
|
2572 | 2571 | In [2]: %parts A B |
|
2573 | 2572 | first A second B |
|
2574 | 2573 | In [3]: %parts A |
|
2575 | 2574 | Incorrect number of arguments: 2 expected. |
|
2576 | 2575 | parts is an alias to: 'echo first %s second %s' |
|
2577 | 2576 | |
|
2578 | 2577 | Note that %l and %s are mutually exclusive. You can only use one or |
|
2579 | 2578 | the other in your aliases. |
|
2580 | 2579 | |
|
2581 | 2580 | Aliases expand Python variables just like system calls using ! or !! |
|
2582 | 2581 | do: all expressions prefixed with '$' get expanded. For details of |
|
2583 | 2582 | the semantic rules, see PEP-215: |
|
2584 | 2583 | http://www.python.org/peps/pep-0215.html. This is the library used by |
|
2585 | 2584 | IPython for variable expansion. If you want to access a true shell |
|
2586 | 2585 | variable, an extra $ is necessary to prevent its expansion by IPython: |
|
2587 | 2586 | |
|
2588 | 2587 | In [6]: alias show echo |
|
2589 | 2588 | In [7]: PATH='A Python string' |
|
2590 | 2589 | In [8]: show $PATH |
|
2591 | 2590 | A Python string |
|
2592 | 2591 | In [9]: show $$PATH |
|
2593 | 2592 | /usr/local/lf9560/bin:/usr/local/intel/compiler70/ia32/bin:... |
|
2594 | 2593 | |
|
2595 | 2594 | You can use the alias facility to acess all of $PATH. See the %rehash |
|
2596 | 2595 | and %rehashx functions, which automatically create aliases for the |
|
2597 | 2596 | contents of your $PATH. |
|
2598 | 2597 | |
|
2599 | 2598 | If called with no parameters, %alias prints the current alias table.""" |
|
2600 | 2599 | |
|
2601 | 2600 | par = parameter_s.strip() |
|
2602 | 2601 | if not par: |
|
2603 | 2602 | stored = self.db.get('stored_aliases', {} ) |
|
2604 | 2603 | aliases = sorted(self.shell.alias_manager.aliases) |
|
2605 | 2604 | # for k, v in stored: |
|
2606 | 2605 | # atab.append(k, v[0]) |
|
2607 | 2606 | |
|
2608 | 2607 | print "Total number of aliases:", len(aliases) |
|
2609 | 2608 | sys.stdout.flush() |
|
2610 | 2609 | return aliases |
|
2611 | 2610 | |
|
2612 | 2611 | # Now try to define a new one |
|
2613 | 2612 | try: |
|
2614 | 2613 | alias,cmd = par.split(None, 1) |
|
2615 | 2614 | except: |
|
2616 | 2615 | print oinspect.getdoc(self.magic_alias) |
|
2617 | 2616 | else: |
|
2618 | 2617 | self.shell.alias_manager.soft_define_alias(alias, cmd) |
|
2619 | 2618 | # end magic_alias |
|
2620 | 2619 | |
|
2621 | 2620 | def magic_unalias(self, parameter_s = ''): |
|
2622 | 2621 | """Remove an alias""" |
|
2623 | 2622 | |
|
2624 | 2623 | aname = parameter_s.strip() |
|
2625 | 2624 | self.shell.alias_manager.undefine_alias(aname) |
|
2626 | 2625 | stored = self.db.get('stored_aliases', {} ) |
|
2627 | 2626 | if aname in stored: |
|
2628 | 2627 | print "Removing %stored alias",aname |
|
2629 | 2628 | del stored[aname] |
|
2630 | 2629 | self.db['stored_aliases'] = stored |
|
2631 | 2630 | |
|
2632 | 2631 | def magic_rehashx(self, parameter_s = ''): |
|
2633 | 2632 | """Update the alias table with all executable files in $PATH. |
|
2634 | 2633 | |
|
2635 | 2634 | This version explicitly checks that every entry in $PATH is a file |
|
2636 | 2635 | with execute access (os.X_OK), so it is much slower than %rehash. |
|
2637 | 2636 | |
|
2638 | 2637 | Under Windows, it checks executability as a match agains a |
|
2639 | 2638 | '|'-separated string of extensions, stored in the IPython config |
|
2640 | 2639 | variable win_exec_ext. This defaults to 'exe|com|bat'. |
|
2641 | 2640 | |
|
2642 | 2641 | This function also resets the root module cache of module completer, |
|
2643 | 2642 | used on slow filesystems. |
|
2644 | 2643 | """ |
|
2645 | 2644 | from IPython.core.alias import InvalidAliasError |
|
2646 | 2645 | |
|
2647 | 2646 | # for the benefit of module completer in ipy_completers.py |
|
2648 | 2647 | del self.db['rootmodules'] |
|
2649 | 2648 | |
|
2650 | 2649 | path = [os.path.abspath(os.path.expanduser(p)) for p in |
|
2651 | 2650 | os.environ.get('PATH','').split(os.pathsep)] |
|
2652 | 2651 | path = filter(os.path.isdir,path) |
|
2653 | 2652 | |
|
2654 | 2653 | syscmdlist = [] |
|
2655 | 2654 | # Now define isexec in a cross platform manner. |
|
2656 | 2655 | if os.name == 'posix': |
|
2657 | 2656 | isexec = lambda fname:os.path.isfile(fname) and \ |
|
2658 | 2657 | os.access(fname,os.X_OK) |
|
2659 | 2658 | else: |
|
2660 | 2659 | try: |
|
2661 | 2660 | winext = os.environ['pathext'].replace(';','|').replace('.','') |
|
2662 | 2661 | except KeyError: |
|
2663 | 2662 | winext = 'exe|com|bat|py' |
|
2664 | 2663 | if 'py' not in winext: |
|
2665 | 2664 | winext += '|py' |
|
2666 | 2665 | execre = re.compile(r'(.*)\.(%s)$' % winext,re.IGNORECASE) |
|
2667 | 2666 | isexec = lambda fname:os.path.isfile(fname) and execre.match(fname) |
|
2668 | 2667 | savedir = os.getcwdu() |
|
2669 | 2668 | |
|
2670 | 2669 | # Now walk the paths looking for executables to alias. |
|
2671 | 2670 | try: |
|
2672 | 2671 | # write the whole loop for posix/Windows so we don't have an if in |
|
2673 | 2672 | # the innermost part |
|
2674 | 2673 | if os.name == 'posix': |
|
2675 | 2674 | for pdir in path: |
|
2676 | 2675 | os.chdir(pdir) |
|
2677 | 2676 | for ff in os.listdir(pdir): |
|
2678 | 2677 | if isexec(ff): |
|
2679 | 2678 | try: |
|
2680 | 2679 | # Removes dots from the name since ipython |
|
2681 | 2680 | # will assume names with dots to be python. |
|
2682 | 2681 | self.shell.alias_manager.define_alias( |
|
2683 | 2682 | ff.replace('.',''), ff) |
|
2684 | 2683 | except InvalidAliasError: |
|
2685 | 2684 | pass |
|
2686 | 2685 | else: |
|
2687 | 2686 | syscmdlist.append(ff) |
|
2688 | 2687 | else: |
|
2689 | 2688 | no_alias = self.shell.alias_manager.no_alias |
|
2690 | 2689 | for pdir in path: |
|
2691 | 2690 | os.chdir(pdir) |
|
2692 | 2691 | for ff in os.listdir(pdir): |
|
2693 | 2692 | base, ext = os.path.splitext(ff) |
|
2694 | 2693 | if isexec(ff) and base.lower() not in no_alias: |
|
2695 | 2694 | if ext.lower() == '.exe': |
|
2696 | 2695 | ff = base |
|
2697 | 2696 | try: |
|
2698 | 2697 | # Removes dots from the name since ipython |
|
2699 | 2698 | # will assume names with dots to be python. |
|
2700 | 2699 | self.shell.alias_manager.define_alias( |
|
2701 | 2700 | base.lower().replace('.',''), ff) |
|
2702 | 2701 | except InvalidAliasError: |
|
2703 | 2702 | pass |
|
2704 | 2703 | syscmdlist.append(ff) |
|
2705 | 2704 | db = self.db |
|
2706 | 2705 | db['syscmdlist'] = syscmdlist |
|
2707 | 2706 | finally: |
|
2708 | 2707 | os.chdir(savedir) |
|
2709 | 2708 | |
|
2710 | 2709 | @skip_doctest |
|
2711 | 2710 | def magic_pwd(self, parameter_s = ''): |
|
2712 | 2711 | """Return the current working directory path. |
|
2713 | 2712 | |
|
2714 | 2713 | Examples |
|
2715 | 2714 | -------- |
|
2716 | 2715 | :: |
|
2717 | 2716 | |
|
2718 | 2717 | In [9]: pwd |
|
2719 | 2718 | Out[9]: '/home/tsuser/sprint/ipython' |
|
2720 | 2719 | """ |
|
2721 | 2720 | return os.getcwdu() |
|
2722 | 2721 | |
|
2723 | 2722 | @skip_doctest |
|
2724 | 2723 | def magic_cd(self, parameter_s=''): |
|
2725 | 2724 | """Change the current working directory. |
|
2726 | 2725 | |
|
2727 | 2726 | This command automatically maintains an internal list of directories |
|
2728 | 2727 | you visit during your IPython session, in the variable _dh. The |
|
2729 | 2728 | command %dhist shows this history nicely formatted. You can also |
|
2730 | 2729 | do 'cd -<tab>' to see directory history conveniently. |
|
2731 | 2730 | |
|
2732 | 2731 | Usage: |
|
2733 | 2732 | |
|
2734 | 2733 | cd 'dir': changes to directory 'dir'. |
|
2735 | 2734 | |
|
2736 | 2735 | cd -: changes to the last visited directory. |
|
2737 | 2736 | |
|
2738 | 2737 | cd -<n>: changes to the n-th directory in the directory history. |
|
2739 | 2738 | |
|
2740 | 2739 | cd --foo: change to directory that matches 'foo' in history |
|
2741 | 2740 | |
|
2742 | 2741 | cd -b <bookmark_name>: jump to a bookmark set by %bookmark |
|
2743 | 2742 | (note: cd <bookmark_name> is enough if there is no |
|
2744 | 2743 | directory <bookmark_name>, but a bookmark with the name exists.) |
|
2745 | 2744 | 'cd -b <tab>' allows you to tab-complete bookmark names. |
|
2746 | 2745 | |
|
2747 | 2746 | Options: |
|
2748 | 2747 | |
|
2749 | 2748 | -q: quiet. Do not print the working directory after the cd command is |
|
2750 | 2749 | executed. By default IPython's cd command does print this directory, |
|
2751 | 2750 | since the default prompts do not display path information. |
|
2752 | 2751 | |
|
2753 | 2752 | Note that !cd doesn't work for this purpose because the shell where |
|
2754 | 2753 | !command runs is immediately discarded after executing 'command'. |
|
2755 | 2754 | |
|
2756 | 2755 | Examples |
|
2757 | 2756 | -------- |
|
2758 | 2757 | :: |
|
2759 | 2758 | |
|
2760 | 2759 | In [10]: cd parent/child |
|
2761 | 2760 | /home/tsuser/parent/child |
|
2762 | 2761 | """ |
|
2763 | 2762 | |
|
2764 | 2763 | parameter_s = parameter_s.strip() |
|
2765 | 2764 | #bkms = self.shell.persist.get("bookmarks",{}) |
|
2766 | 2765 | |
|
2767 | 2766 | oldcwd = os.getcwdu() |
|
2768 | 2767 | numcd = re.match(r'(-)(\d+)$',parameter_s) |
|
2769 | 2768 | # jump in directory history by number |
|
2770 | 2769 | if numcd: |
|
2771 | 2770 | nn = int(numcd.group(2)) |
|
2772 | 2771 | try: |
|
2773 | 2772 | ps = self.shell.user_ns['_dh'][nn] |
|
2774 | 2773 | except IndexError: |
|
2775 | 2774 | print 'The requested directory does not exist in history.' |
|
2776 | 2775 | return |
|
2777 | 2776 | else: |
|
2778 | 2777 | opts = {} |
|
2779 | 2778 | elif parameter_s.startswith('--'): |
|
2780 | 2779 | ps = None |
|
2781 | 2780 | fallback = None |
|
2782 | 2781 | pat = parameter_s[2:] |
|
2783 | 2782 | dh = self.shell.user_ns['_dh'] |
|
2784 | 2783 | # first search only by basename (last component) |
|
2785 | 2784 | for ent in reversed(dh): |
|
2786 | 2785 | if pat in os.path.basename(ent) and os.path.isdir(ent): |
|
2787 | 2786 | ps = ent |
|
2788 | 2787 | break |
|
2789 | 2788 | |
|
2790 | 2789 | if fallback is None and pat in ent and os.path.isdir(ent): |
|
2791 | 2790 | fallback = ent |
|
2792 | 2791 | |
|
2793 | 2792 | # if we have no last part match, pick the first full path match |
|
2794 | 2793 | if ps is None: |
|
2795 | 2794 | ps = fallback |
|
2796 | 2795 | |
|
2797 | 2796 | if ps is None: |
|
2798 | 2797 | print "No matching entry in directory history" |
|
2799 | 2798 | return |
|
2800 | 2799 | else: |
|
2801 | 2800 | opts = {} |
|
2802 | 2801 | |
|
2803 | 2802 | |
|
2804 | 2803 | else: |
|
2805 | 2804 | #turn all non-space-escaping backslashes to slashes, |
|
2806 | 2805 | # for c:\windows\directory\names\ |
|
2807 | 2806 | parameter_s = re.sub(r'\\(?! )','/', parameter_s) |
|
2808 | 2807 | opts,ps = self.parse_options(parameter_s,'qb',mode='string') |
|
2809 | 2808 | # jump to previous |
|
2810 | 2809 | if ps == '-': |
|
2811 | 2810 | try: |
|
2812 | 2811 | ps = self.shell.user_ns['_dh'][-2] |
|
2813 | 2812 | except IndexError: |
|
2814 | 2813 | raise UsageError('%cd -: No previous directory to change to.') |
|
2815 | 2814 | # jump to bookmark if needed |
|
2816 | 2815 | else: |
|
2817 | 2816 | if not os.path.isdir(ps) or opts.has_key('b'): |
|
2818 | 2817 | bkms = self.db.get('bookmarks', {}) |
|
2819 | 2818 | |
|
2820 | 2819 | if bkms.has_key(ps): |
|
2821 | 2820 | target = bkms[ps] |
|
2822 | 2821 | print '(bookmark:%s) -> %s' % (ps,target) |
|
2823 | 2822 | ps = target |
|
2824 | 2823 | else: |
|
2825 | 2824 | if opts.has_key('b'): |
|
2826 | 2825 | raise UsageError("Bookmark '%s' not found. " |
|
2827 | 2826 | "Use '%%bookmark -l' to see your bookmarks." % ps) |
|
2828 | 2827 | |
|
2829 | 2828 | # strip extra quotes on Windows, because os.chdir doesn't like them |
|
2830 | 2829 | if sys.platform == 'win32': |
|
2831 | 2830 | ps = ps.strip('\'"') |
|
2832 | 2831 | # at this point ps should point to the target dir |
|
2833 | 2832 | if ps: |
|
2834 | 2833 | try: |
|
2835 | 2834 | os.chdir(os.path.expanduser(ps)) |
|
2836 | 2835 | if hasattr(self.shell, 'term_title') and self.shell.term_title: |
|
2837 | 2836 | set_term_title('IPython: ' + abbrev_cwd()) |
|
2838 | 2837 | except OSError: |
|
2839 | 2838 | print sys.exc_info()[1] |
|
2840 | 2839 | else: |
|
2841 | 2840 | cwd = os.getcwdu() |
|
2842 | 2841 | dhist = self.shell.user_ns['_dh'] |
|
2843 | 2842 | if oldcwd != cwd: |
|
2844 | 2843 | dhist.append(cwd) |
|
2845 | 2844 | self.db['dhist'] = compress_dhist(dhist)[-100:] |
|
2846 | 2845 | |
|
2847 | 2846 | else: |
|
2848 | 2847 | os.chdir(self.shell.home_dir) |
|
2849 | 2848 | if hasattr(self.shell, 'term_title') and self.shell.term_title: |
|
2850 | 2849 | set_term_title('IPython: ' + '~') |
|
2851 | 2850 | cwd = os.getcwdu() |
|
2852 | 2851 | dhist = self.shell.user_ns['_dh'] |
|
2853 | 2852 | |
|
2854 | 2853 | if oldcwd != cwd: |
|
2855 | 2854 | dhist.append(cwd) |
|
2856 | 2855 | self.db['dhist'] = compress_dhist(dhist)[-100:] |
|
2857 | 2856 | if not 'q' in opts and self.shell.user_ns['_dh']: |
|
2858 | 2857 | print self.shell.user_ns['_dh'][-1] |
|
2859 | 2858 | |
|
2860 | 2859 | |
|
2861 | 2860 | def magic_env(self, parameter_s=''): |
|
2862 | 2861 | """List environment variables.""" |
|
2863 | 2862 | |
|
2864 | 2863 | return os.environ.data |
|
2865 | 2864 | |
|
2866 | 2865 | def magic_pushd(self, parameter_s=''): |
|
2867 | 2866 | """Place the current dir on stack and change directory. |
|
2868 | 2867 | |
|
2869 | 2868 | Usage:\\ |
|
2870 | 2869 | %pushd ['dirname'] |
|
2871 | 2870 | """ |
|
2872 | 2871 | |
|
2873 | 2872 | dir_s = self.shell.dir_stack |
|
2874 | 2873 | tgt = os.path.expanduser(parameter_s) |
|
2875 | 2874 | cwd = os.getcwdu().replace(self.home_dir,'~') |
|
2876 | 2875 | if tgt: |
|
2877 | 2876 | self.magic_cd(parameter_s) |
|
2878 | 2877 | dir_s.insert(0,cwd) |
|
2879 | 2878 | return self.magic_dirs() |
|
2880 | 2879 | |
|
2881 | 2880 | def magic_popd(self, parameter_s=''): |
|
2882 | 2881 | """Change to directory popped off the top of the stack. |
|
2883 | 2882 | """ |
|
2884 | 2883 | if not self.shell.dir_stack: |
|
2885 | 2884 | raise UsageError("%popd on empty stack") |
|
2886 | 2885 | top = self.shell.dir_stack.pop(0) |
|
2887 | 2886 | self.magic_cd(top) |
|
2888 | 2887 | print "popd ->",top |
|
2889 | 2888 | |
|
2890 | 2889 | def magic_dirs(self, parameter_s=''): |
|
2891 | 2890 | """Return the current directory stack.""" |
|
2892 | 2891 | |
|
2893 | 2892 | return self.shell.dir_stack |
|
2894 | 2893 | |
|
2895 | 2894 | def magic_dhist(self, parameter_s=''): |
|
2896 | 2895 | """Print your history of visited directories. |
|
2897 | 2896 | |
|
2898 | 2897 | %dhist -> print full history\\ |
|
2899 | 2898 | %dhist n -> print last n entries only\\ |
|
2900 | 2899 | %dhist n1 n2 -> print entries between n1 and n2 (n1 not included)\\ |
|
2901 | 2900 | |
|
2902 | 2901 | This history is automatically maintained by the %cd command, and |
|
2903 | 2902 | always available as the global list variable _dh. You can use %cd -<n> |
|
2904 | 2903 | to go to directory number <n>. |
|
2905 | 2904 | |
|
2906 | 2905 | Note that most of time, you should view directory history by entering |
|
2907 | 2906 | cd -<TAB>. |
|
2908 | 2907 | |
|
2909 | 2908 | """ |
|
2910 | 2909 | |
|
2911 | 2910 | dh = self.shell.user_ns['_dh'] |
|
2912 | 2911 | if parameter_s: |
|
2913 | 2912 | try: |
|
2914 | 2913 | args = map(int,parameter_s.split()) |
|
2915 | 2914 | except: |
|
2916 | 2915 | self.arg_err(Magic.magic_dhist) |
|
2917 | 2916 | return |
|
2918 | 2917 | if len(args) == 1: |
|
2919 | 2918 | ini,fin = max(len(dh)-(args[0]),0),len(dh) |
|
2920 | 2919 | elif len(args) == 2: |
|
2921 | 2920 | ini,fin = args |
|
2922 | 2921 | else: |
|
2923 | 2922 | self.arg_err(Magic.magic_dhist) |
|
2924 | 2923 | return |
|
2925 | 2924 | else: |
|
2926 | 2925 | ini,fin = 0,len(dh) |
|
2927 | 2926 | nlprint(dh, |
|
2928 | 2927 | header = 'Directory history (kept in _dh)', |
|
2929 | 2928 | start=ini,stop=fin) |
|
2930 | 2929 | |
|
2931 | 2930 | @skip_doctest |
|
2932 | 2931 | def magic_sc(self, parameter_s=''): |
|
2933 | 2932 | """Shell capture - execute a shell command and capture its output. |
|
2934 | 2933 | |
|
2935 | 2934 | DEPRECATED. Suboptimal, retained for backwards compatibility. |
|
2936 | 2935 | |
|
2937 | 2936 | You should use the form 'var = !command' instead. Example: |
|
2938 | 2937 | |
|
2939 | 2938 | "%sc -l myfiles = ls ~" should now be written as |
|
2940 | 2939 | |
|
2941 | 2940 | "myfiles = !ls ~" |
|
2942 | 2941 | |
|
2943 | 2942 | myfiles.s, myfiles.l and myfiles.n still apply as documented |
|
2944 | 2943 | below. |
|
2945 | 2944 | |
|
2946 | 2945 | -- |
|
2947 | 2946 | %sc [options] varname=command |
|
2948 | 2947 | |
|
2949 | 2948 | IPython will run the given command using commands.getoutput(), and |
|
2950 | 2949 | will then update the user's interactive namespace with a variable |
|
2951 | 2950 | called varname, containing the value of the call. Your command can |
|
2952 | 2951 | contain shell wildcards, pipes, etc. |
|
2953 | 2952 | |
|
2954 | 2953 | The '=' sign in the syntax is mandatory, and the variable name you |
|
2955 | 2954 | supply must follow Python's standard conventions for valid names. |
|
2956 | 2955 | |
|
2957 | 2956 | (A special format without variable name exists for internal use) |
|
2958 | 2957 | |
|
2959 | 2958 | Options: |
|
2960 | 2959 | |
|
2961 | 2960 | -l: list output. Split the output on newlines into a list before |
|
2962 | 2961 | assigning it to the given variable. By default the output is stored |
|
2963 | 2962 | as a single string. |
|
2964 | 2963 | |
|
2965 | 2964 | -v: verbose. Print the contents of the variable. |
|
2966 | 2965 | |
|
2967 | 2966 | In most cases you should not need to split as a list, because the |
|
2968 | 2967 | returned value is a special type of string which can automatically |
|
2969 | 2968 | provide its contents either as a list (split on newlines) or as a |
|
2970 | 2969 | space-separated string. These are convenient, respectively, either |
|
2971 | 2970 | for sequential processing or to be passed to a shell command. |
|
2972 | 2971 | |
|
2973 | 2972 | For example: |
|
2974 | 2973 | |
|
2975 | 2974 | # all-random |
|
2976 | 2975 | |
|
2977 | 2976 | # Capture into variable a |
|
2978 | 2977 | In [1]: sc a=ls *py |
|
2979 | 2978 | |
|
2980 | 2979 | # a is a string with embedded newlines |
|
2981 | 2980 | In [2]: a |
|
2982 | 2981 | Out[2]: 'setup.py\\nwin32_manual_post_install.py' |
|
2983 | 2982 | |
|
2984 | 2983 | # which can be seen as a list: |
|
2985 | 2984 | In [3]: a.l |
|
2986 | 2985 | Out[3]: ['setup.py', 'win32_manual_post_install.py'] |
|
2987 | 2986 | |
|
2988 | 2987 | # or as a whitespace-separated string: |
|
2989 | 2988 | In [4]: a.s |
|
2990 | 2989 | Out[4]: 'setup.py win32_manual_post_install.py' |
|
2991 | 2990 | |
|
2992 | 2991 | # a.s is useful to pass as a single command line: |
|
2993 | 2992 | In [5]: !wc -l $a.s |
|
2994 | 2993 | 146 setup.py |
|
2995 | 2994 | 130 win32_manual_post_install.py |
|
2996 | 2995 | 276 total |
|
2997 | 2996 | |
|
2998 | 2997 | # while the list form is useful to loop over: |
|
2999 | 2998 | In [6]: for f in a.l: |
|
3000 | 2999 | ...: !wc -l $f |
|
3001 | 3000 | ...: |
|
3002 | 3001 | 146 setup.py |
|
3003 | 3002 | 130 win32_manual_post_install.py |
|
3004 | 3003 | |
|
3005 | 3004 | Similiarly, the lists returned by the -l option are also special, in |
|
3006 | 3005 | the sense that you can equally invoke the .s attribute on them to |
|
3007 | 3006 | automatically get a whitespace-separated string from their contents: |
|
3008 | 3007 | |
|
3009 | 3008 | In [7]: sc -l b=ls *py |
|
3010 | 3009 | |
|
3011 | 3010 | In [8]: b |
|
3012 | 3011 | Out[8]: ['setup.py', 'win32_manual_post_install.py'] |
|
3013 | 3012 | |
|
3014 | 3013 | In [9]: b.s |
|
3015 | 3014 | Out[9]: 'setup.py win32_manual_post_install.py' |
|
3016 | 3015 | |
|
3017 | 3016 | In summary, both the lists and strings used for ouptut capture have |
|
3018 | 3017 | the following special attributes: |
|
3019 | 3018 | |
|
3020 | 3019 | .l (or .list) : value as list. |
|
3021 | 3020 | .n (or .nlstr): value as newline-separated string. |
|
3022 | 3021 | .s (or .spstr): value as space-separated string. |
|
3023 | 3022 | """ |
|
3024 | 3023 | |
|
3025 | 3024 | opts,args = self.parse_options(parameter_s,'lv') |
|
3026 | 3025 | # Try to get a variable name and command to run |
|
3027 | 3026 | try: |
|
3028 | 3027 | # the variable name must be obtained from the parse_options |
|
3029 | 3028 | # output, which uses shlex.split to strip options out. |
|
3030 | 3029 | var,_ = args.split('=',1) |
|
3031 | 3030 | var = var.strip() |
|
3032 | 3031 | # But the the command has to be extracted from the original input |
|
3033 | 3032 | # parameter_s, not on what parse_options returns, to avoid the |
|
3034 | 3033 | # quote stripping which shlex.split performs on it. |
|
3035 | 3034 | _,cmd = parameter_s.split('=',1) |
|
3036 | 3035 | except ValueError: |
|
3037 | 3036 | var,cmd = '','' |
|
3038 | 3037 | # If all looks ok, proceed |
|
3039 | 3038 | split = 'l' in opts |
|
3040 | 3039 | out = self.shell.getoutput(cmd, split=split) |
|
3041 | 3040 | if opts.has_key('v'): |
|
3042 | 3041 | print '%s ==\n%s' % (var,pformat(out)) |
|
3043 | 3042 | if var: |
|
3044 | 3043 | self.shell.user_ns.update({var:out}) |
|
3045 | 3044 | else: |
|
3046 | 3045 | return out |
|
3047 | 3046 | |
|
3048 | 3047 | def magic_sx(self, parameter_s=''): |
|
3049 | 3048 | """Shell execute - run a shell command and capture its output. |
|
3050 | 3049 | |
|
3051 | 3050 | %sx command |
|
3052 | 3051 | |
|
3053 | 3052 | IPython will run the given command using commands.getoutput(), and |
|
3054 | 3053 | return the result formatted as a list (split on '\\n'). Since the |
|
3055 | 3054 | output is _returned_, it will be stored in ipython's regular output |
|
3056 | 3055 | cache Out[N] and in the '_N' automatic variables. |
|
3057 | 3056 | |
|
3058 | 3057 | Notes: |
|
3059 | 3058 | |
|
3060 | 3059 | 1) If an input line begins with '!!', then %sx is automatically |
|
3061 | 3060 | invoked. That is, while: |
|
3062 | 3061 | !ls |
|
3063 | 3062 | causes ipython to simply issue system('ls'), typing |
|
3064 | 3063 | !!ls |
|
3065 | 3064 | is a shorthand equivalent to: |
|
3066 | 3065 | %sx ls |
|
3067 | 3066 | |
|
3068 | 3067 | 2) %sx differs from %sc in that %sx automatically splits into a list, |
|
3069 | 3068 | like '%sc -l'. The reason for this is to make it as easy as possible |
|
3070 | 3069 | to process line-oriented shell output via further python commands. |
|
3071 | 3070 | %sc is meant to provide much finer control, but requires more |
|
3072 | 3071 | typing. |
|
3073 | 3072 | |
|
3074 | 3073 | 3) Just like %sc -l, this is a list with special attributes: |
|
3075 | 3074 | |
|
3076 | 3075 | .l (or .list) : value as list. |
|
3077 | 3076 | .n (or .nlstr): value as newline-separated string. |
|
3078 | 3077 | .s (or .spstr): value as whitespace-separated string. |
|
3079 | 3078 | |
|
3080 | 3079 | This is very useful when trying to use such lists as arguments to |
|
3081 | 3080 | system commands.""" |
|
3082 | 3081 | |
|
3083 | 3082 | if parameter_s: |
|
3084 | 3083 | return self.shell.getoutput(parameter_s) |
|
3085 | 3084 | |
|
3086 | 3085 | |
|
3087 | 3086 | def magic_bookmark(self, parameter_s=''): |
|
3088 | 3087 | """Manage IPython's bookmark system. |
|
3089 | 3088 | |
|
3090 | 3089 | %bookmark <name> - set bookmark to current dir |
|
3091 | 3090 | %bookmark <name> <dir> - set bookmark to <dir> |
|
3092 | 3091 | %bookmark -l - list all bookmarks |
|
3093 | 3092 | %bookmark -d <name> - remove bookmark |
|
3094 | 3093 | %bookmark -r - remove all bookmarks |
|
3095 | 3094 | |
|
3096 | 3095 | You can later on access a bookmarked folder with: |
|
3097 | 3096 | %cd -b <name> |
|
3098 | 3097 | or simply '%cd <name>' if there is no directory called <name> AND |
|
3099 | 3098 | there is such a bookmark defined. |
|
3100 | 3099 | |
|
3101 | 3100 | Your bookmarks persist through IPython sessions, but they are |
|
3102 | 3101 | associated with each profile.""" |
|
3103 | 3102 | |
|
3104 | 3103 | opts,args = self.parse_options(parameter_s,'drl',mode='list') |
|
3105 | 3104 | if len(args) > 2: |
|
3106 | 3105 | raise UsageError("%bookmark: too many arguments") |
|
3107 | 3106 | |
|
3108 | 3107 | bkms = self.db.get('bookmarks',{}) |
|
3109 | 3108 | |
|
3110 | 3109 | if opts.has_key('d'): |
|
3111 | 3110 | try: |
|
3112 | 3111 | todel = args[0] |
|
3113 | 3112 | except IndexError: |
|
3114 | 3113 | raise UsageError( |
|
3115 | 3114 | "%bookmark -d: must provide a bookmark to delete") |
|
3116 | 3115 | else: |
|
3117 | 3116 | try: |
|
3118 | 3117 | del bkms[todel] |
|
3119 | 3118 | except KeyError: |
|
3120 | 3119 | raise UsageError( |
|
3121 | 3120 | "%%bookmark -d: Can't delete bookmark '%s'" % todel) |
|
3122 | 3121 | |
|
3123 | 3122 | elif opts.has_key('r'): |
|
3124 | 3123 | bkms = {} |
|
3125 | 3124 | elif opts.has_key('l'): |
|
3126 | 3125 | bks = bkms.keys() |
|
3127 | 3126 | bks.sort() |
|
3128 | 3127 | if bks: |
|
3129 | 3128 | size = max(map(len,bks)) |
|
3130 | 3129 | else: |
|
3131 | 3130 | size = 0 |
|
3132 | 3131 | fmt = '%-'+str(size)+'s -> %s' |
|
3133 | 3132 | print 'Current bookmarks:' |
|
3134 | 3133 | for bk in bks: |
|
3135 | 3134 | print fmt % (bk,bkms[bk]) |
|
3136 | 3135 | else: |
|
3137 | 3136 | if not args: |
|
3138 | 3137 | raise UsageError("%bookmark: You must specify the bookmark name") |
|
3139 | 3138 | elif len(args)==1: |
|
3140 | 3139 | bkms[args[0]] = os.getcwdu() |
|
3141 | 3140 | elif len(args)==2: |
|
3142 | 3141 | bkms[args[0]] = args[1] |
|
3143 | 3142 | self.db['bookmarks'] = bkms |
|
3144 | 3143 | |
|
3145 | 3144 | def magic_pycat(self, parameter_s=''): |
|
3146 | 3145 | """Show a syntax-highlighted file through a pager. |
|
3147 | 3146 | |
|
3148 | 3147 | This magic is similar to the cat utility, but it will assume the file |
|
3149 | 3148 | to be Python source and will show it with syntax highlighting. """ |
|
3150 | 3149 | |
|
3151 | 3150 | try: |
|
3152 | 3151 | filename = get_py_filename(parameter_s) |
|
3153 | 3152 | cont = file_read(filename) |
|
3154 | 3153 | except IOError: |
|
3155 | 3154 | try: |
|
3156 | 3155 | cont = eval(parameter_s,self.user_ns) |
|
3157 | 3156 | except NameError: |
|
3158 | 3157 | cont = None |
|
3159 | 3158 | if cont is None: |
|
3160 | 3159 | print "Error: no such file or variable" |
|
3161 | 3160 | return |
|
3162 | 3161 | |
|
3163 | 3162 | page.page(self.shell.pycolorize(cont)) |
|
3164 | 3163 | |
|
3165 | 3164 | def _rerun_pasted(self): |
|
3166 | 3165 | """ Rerun a previously pasted command. |
|
3167 | 3166 | """ |
|
3168 | 3167 | b = self.user_ns.get('pasted_block', None) |
|
3169 | 3168 | if b is None: |
|
3170 | 3169 | raise UsageError('No previous pasted block available') |
|
3171 | 3170 | print "Re-executing '%s...' (%d chars)"% (b.split('\n',1)[0], len(b)) |
|
3172 | 3171 | exec b in self.user_ns |
|
3173 | 3172 | |
|
3174 | 3173 | def _get_pasted_lines(self, sentinel): |
|
3175 | 3174 | """ Yield pasted lines until the user enters the given sentinel value. |
|
3176 | 3175 | """ |
|
3177 | 3176 | from IPython.core import interactiveshell |
|
3178 | 3177 | print "Pasting code; enter '%s' alone on the line to stop." % sentinel |
|
3179 | 3178 | while True: |
|
3180 | 3179 | l = interactiveshell.raw_input_original(':') |
|
3181 | 3180 | if l == sentinel: |
|
3182 | 3181 | return |
|
3183 | 3182 | else: |
|
3184 | 3183 | yield l |
|
3185 | 3184 | |
|
3186 | 3185 | def _strip_pasted_lines_for_code(self, raw_lines): |
|
3187 | 3186 | """ Strip non-code parts of a sequence of lines to return a block of |
|
3188 | 3187 | code. |
|
3189 | 3188 | """ |
|
3190 | 3189 | # Regular expressions that declare text we strip from the input: |
|
3191 | 3190 | strip_re = [r'^\s*In \[\d+\]:', # IPython input prompt |
|
3192 | 3191 | r'^\s*(\s?>)+', # Python input prompt |
|
3193 | 3192 | r'^\s*\.{3,}', # Continuation prompts |
|
3194 | 3193 | r'^\++', |
|
3195 | 3194 | ] |
|
3196 | 3195 | |
|
3197 | 3196 | strip_from_start = map(re.compile,strip_re) |
|
3198 | 3197 | |
|
3199 | 3198 | lines = [] |
|
3200 | 3199 | for l in raw_lines: |
|
3201 | 3200 | for pat in strip_from_start: |
|
3202 | 3201 | l = pat.sub('',l) |
|
3203 | 3202 | lines.append(l) |
|
3204 | 3203 | |
|
3205 | 3204 | block = "\n".join(lines) + '\n' |
|
3206 | 3205 | #print "block:\n",block |
|
3207 | 3206 | return block |
|
3208 | 3207 | |
|
3209 | 3208 | def _execute_block(self, block, par): |
|
3210 | 3209 | """ Execute a block, or store it in a variable, per the user's request. |
|
3211 | 3210 | """ |
|
3212 | 3211 | if not par: |
|
3213 | 3212 | b = textwrap.dedent(block) |
|
3214 | 3213 | self.user_ns['pasted_block'] = b |
|
3215 | 3214 | exec b in self.user_ns |
|
3216 | 3215 | else: |
|
3217 | 3216 | self.user_ns[par] = SList(block.splitlines()) |
|
3218 | 3217 | print "Block assigned to '%s'" % par |
|
3219 | 3218 | |
|
3220 | 3219 | def magic_quickref(self,arg): |
|
3221 | 3220 | """ Show a quick reference sheet """ |
|
3222 | 3221 | import IPython.core.usage |
|
3223 | 3222 | qr = IPython.core.usage.quick_reference + self.magic_magic('-brief') |
|
3224 | 3223 | |
|
3225 | 3224 | page.page(qr) |
|
3226 | 3225 | |
|
3227 | 3226 | def magic_doctest_mode(self,parameter_s=''): |
|
3228 | 3227 | """Toggle doctest mode on and off. |
|
3229 | 3228 | |
|
3230 | 3229 | This mode is intended to make IPython behave as much as possible like a |
|
3231 | 3230 | plain Python shell, from the perspective of how its prompts, exceptions |
|
3232 | 3231 | and output look. This makes it easy to copy and paste parts of a |
|
3233 | 3232 | session into doctests. It does so by: |
|
3234 | 3233 | |
|
3235 | 3234 | - Changing the prompts to the classic ``>>>`` ones. |
|
3236 | 3235 | - Changing the exception reporting mode to 'Plain'. |
|
3237 | 3236 | - Disabling pretty-printing of output. |
|
3238 | 3237 | |
|
3239 | 3238 | Note that IPython also supports the pasting of code snippets that have |
|
3240 | 3239 | leading '>>>' and '...' prompts in them. This means that you can paste |
|
3241 | 3240 | doctests from files or docstrings (even if they have leading |
|
3242 | 3241 | whitespace), and the code will execute correctly. You can then use |
|
3243 | 3242 | '%history -t' to see the translated history; this will give you the |
|
3244 | 3243 | input after removal of all the leading prompts and whitespace, which |
|
3245 | 3244 | can be pasted back into an editor. |
|
3246 | 3245 | |
|
3247 | 3246 | With these features, you can switch into this mode easily whenever you |
|
3248 | 3247 | need to do testing and changes to doctests, without having to leave |
|
3249 | 3248 | your existing IPython session. |
|
3250 | 3249 | """ |
|
3251 | 3250 | |
|
3252 | 3251 | from IPython.utils.ipstruct import Struct |
|
3253 | 3252 | |
|
3254 | 3253 | # Shorthands |
|
3255 | 3254 | shell = self.shell |
|
3256 | 3255 | oc = shell.displayhook |
|
3257 | 3256 | meta = shell.meta |
|
3258 | 3257 | disp_formatter = self.shell.display_formatter |
|
3259 | 3258 | ptformatter = disp_formatter.formatters['text/plain'] |
|
3260 | 3259 | # dstore is a data store kept in the instance metadata bag to track any |
|
3261 | 3260 | # changes we make, so we can undo them later. |
|
3262 | 3261 | dstore = meta.setdefault('doctest_mode',Struct()) |
|
3263 | 3262 | save_dstore = dstore.setdefault |
|
3264 | 3263 | |
|
3265 | 3264 | # save a few values we'll need to recover later |
|
3266 | 3265 | mode = save_dstore('mode',False) |
|
3267 | 3266 | save_dstore('rc_pprint',ptformatter.pprint) |
|
3268 | 3267 | save_dstore('xmode',shell.InteractiveTB.mode) |
|
3269 | 3268 | save_dstore('rc_separate_out',shell.separate_out) |
|
3270 | 3269 | save_dstore('rc_separate_out2',shell.separate_out2) |
|
3271 | 3270 | save_dstore('rc_prompts_pad_left',shell.prompts_pad_left) |
|
3272 | 3271 | save_dstore('rc_separate_in',shell.separate_in) |
|
3273 | 3272 | save_dstore('rc_plain_text_only',disp_formatter.plain_text_only) |
|
3274 | 3273 | |
|
3275 | 3274 | if mode == False: |
|
3276 | 3275 | # turn on |
|
3277 | 3276 | oc.prompt1.p_template = '>>> ' |
|
3278 | 3277 | oc.prompt2.p_template = '... ' |
|
3279 | 3278 | oc.prompt_out.p_template = '' |
|
3280 | 3279 | |
|
3281 | 3280 | # Prompt separators like plain python |
|
3282 | 3281 | oc.input_sep = oc.prompt1.sep = '' |
|
3283 | 3282 | oc.output_sep = '' |
|
3284 | 3283 | oc.output_sep2 = '' |
|
3285 | 3284 | |
|
3286 | 3285 | oc.prompt1.pad_left = oc.prompt2.pad_left = \ |
|
3287 | 3286 | oc.prompt_out.pad_left = False |
|
3288 | 3287 | |
|
3289 | 3288 | ptformatter.pprint = False |
|
3290 | 3289 | disp_formatter.plain_text_only = True |
|
3291 | 3290 | |
|
3292 | 3291 | shell.magic_xmode('Plain') |
|
3293 | 3292 | else: |
|
3294 | 3293 | # turn off |
|
3295 | 3294 | oc.prompt1.p_template = shell.prompt_in1 |
|
3296 | 3295 | oc.prompt2.p_template = shell.prompt_in2 |
|
3297 | 3296 | oc.prompt_out.p_template = shell.prompt_out |
|
3298 | 3297 | |
|
3299 | 3298 | oc.input_sep = oc.prompt1.sep = dstore.rc_separate_in |
|
3300 | 3299 | |
|
3301 | 3300 | oc.output_sep = dstore.rc_separate_out |
|
3302 | 3301 | oc.output_sep2 = dstore.rc_separate_out2 |
|
3303 | 3302 | |
|
3304 | 3303 | oc.prompt1.pad_left = oc.prompt2.pad_left = \ |
|
3305 | 3304 | oc.prompt_out.pad_left = dstore.rc_prompts_pad_left |
|
3306 | 3305 | |
|
3307 | 3306 | ptformatter.pprint = dstore.rc_pprint |
|
3308 | 3307 | disp_formatter.plain_text_only = dstore.rc_plain_text_only |
|
3309 | 3308 | |
|
3310 | 3309 | shell.magic_xmode(dstore.xmode) |
|
3311 | 3310 | |
|
3312 | 3311 | # Store new mode and inform |
|
3313 | 3312 | dstore.mode = bool(1-int(mode)) |
|
3314 | 3313 | mode_label = ['OFF','ON'][dstore.mode] |
|
3315 | 3314 | print 'Doctest mode is:', mode_label |
|
3316 | 3315 | |
|
3317 | 3316 | def magic_gui(self, parameter_s=''): |
|
3318 | 3317 | """Enable or disable IPython GUI event loop integration. |
|
3319 | 3318 | |
|
3320 | 3319 | %gui [GUINAME] |
|
3321 | 3320 | |
|
3322 | 3321 | This magic replaces IPython's threaded shells that were activated |
|
3323 | 3322 | using the (pylab/wthread/etc.) command line flags. GUI toolkits |
|
3324 | 3323 | can now be enabled, disabled and changed at runtime and keyboard |
|
3325 | 3324 | interrupts should work without any problems. The following toolkits |
|
3326 | 3325 | are supported: wxPython, PyQt4, PyGTK, and Tk:: |
|
3327 | 3326 | |
|
3328 | 3327 | %gui wx # enable wxPython event loop integration |
|
3329 | 3328 | %gui qt4|qt # enable PyQt4 event loop integration |
|
3330 | 3329 | %gui gtk # enable PyGTK event loop integration |
|
3331 | 3330 | %gui tk # enable Tk event loop integration |
|
3332 | 3331 | %gui # disable all event loop integration |
|
3333 | 3332 | |
|
3334 | 3333 | WARNING: after any of these has been called you can simply create |
|
3335 | 3334 | an application object, but DO NOT start the event loop yourself, as |
|
3336 | 3335 | we have already handled that. |
|
3337 | 3336 | """ |
|
3338 | 3337 | from IPython.lib.inputhook import enable_gui |
|
3339 | 3338 | opts, arg = self.parse_options(parameter_s, '') |
|
3340 | 3339 | if arg=='': arg = None |
|
3341 | 3340 | return enable_gui(arg) |
|
3342 | 3341 | |
|
3343 | 3342 | def magic_load_ext(self, module_str): |
|
3344 | 3343 | """Load an IPython extension by its module name.""" |
|
3345 | 3344 | return self.extension_manager.load_extension(module_str) |
|
3346 | 3345 | |
|
3347 | 3346 | def magic_unload_ext(self, module_str): |
|
3348 | 3347 | """Unload an IPython extension by its module name.""" |
|
3349 | 3348 | self.extension_manager.unload_extension(module_str) |
|
3350 | 3349 | |
|
3351 | 3350 | def magic_reload_ext(self, module_str): |
|
3352 | 3351 | """Reload an IPython extension by its module name.""" |
|
3353 | 3352 | self.extension_manager.reload_extension(module_str) |
|
3354 | 3353 | |
|
3355 | 3354 | @skip_doctest |
|
3356 | 3355 | def magic_install_profiles(self, s): |
|
3357 | 3356 | """Install the default IPython profiles into the .ipython dir. |
|
3358 | 3357 | |
|
3359 | 3358 | If the default profiles have already been installed, they will not |
|
3360 | 3359 | be overwritten. You can force overwriting them by using the ``-o`` |
|
3361 | 3360 | option:: |
|
3362 | 3361 | |
|
3363 | 3362 | In [1]: %install_profiles -o |
|
3364 | 3363 | """ |
|
3365 | 3364 | if '-o' in s: |
|
3366 | 3365 | overwrite = True |
|
3367 | 3366 | else: |
|
3368 | 3367 | overwrite = False |
|
3369 | 3368 | from IPython.config import profile |
|
3370 | 3369 | profile_dir = os.path.dirname(profile.__file__) |
|
3371 | 3370 | ipython_dir = self.ipython_dir |
|
3372 | 3371 | print "Installing profiles to: %s [overwrite=%s]"%(ipython_dir,overwrite) |
|
3373 | 3372 | for src in os.listdir(profile_dir): |
|
3374 | 3373 | if src.startswith('profile_'): |
|
3375 | 3374 | name = src.replace('profile_', '') |
|
3376 | 3375 | print " %s"%name |
|
3377 | 3376 | pd = ProfileDir.create_profile_dir_by_name(ipython_dir, name) |
|
3378 | 3377 | pd.copy_config_file('ipython_config.py', path=src, |
|
3379 | 3378 | overwrite=overwrite) |
|
3380 | 3379 | |
|
3381 | 3380 | @skip_doctest |
|
3382 | 3381 | def magic_install_default_config(self, s): |
|
3383 | 3382 | """Install IPython's default config file into the .ipython dir. |
|
3384 | 3383 | |
|
3385 | 3384 | If the default config file (:file:`ipython_config.py`) is already |
|
3386 | 3385 | installed, it will not be overwritten. You can force overwriting |
|
3387 | 3386 | by using the ``-o`` option:: |
|
3388 | 3387 | |
|
3389 | 3388 | In [1]: %install_default_config |
|
3390 | 3389 | """ |
|
3391 | 3390 | if '-o' in s: |
|
3392 | 3391 | overwrite = True |
|
3393 | 3392 | else: |
|
3394 | 3393 | overwrite = False |
|
3395 | 3394 | pd = self.shell.profile_dir |
|
3396 | 3395 | print "Installing default config file in: %s" % pd.location |
|
3397 | 3396 | pd.copy_config_file('ipython_config.py', overwrite=overwrite) |
|
3398 | 3397 | |
|
3399 | 3398 | # Pylab support: simple wrappers that activate pylab, load gui input |
|
3400 | 3399 | # handling and modify slightly %run |
|
3401 | 3400 | |
|
3402 | 3401 | @skip_doctest |
|
3403 | 3402 | def _pylab_magic_run(self, parameter_s=''): |
|
3404 | 3403 | Magic.magic_run(self, parameter_s, |
|
3405 | 3404 | runner=mpl_runner(self.shell.safe_execfile)) |
|
3406 | 3405 | |
|
3407 | 3406 | _pylab_magic_run.__doc__ = magic_run.__doc__ |
|
3408 | 3407 | |
|
3409 | 3408 | @skip_doctest |
|
3410 | 3409 | def magic_pylab(self, s): |
|
3411 | 3410 | """Load numpy and matplotlib to work interactively. |
|
3412 | 3411 | |
|
3413 | 3412 | %pylab [GUINAME] |
|
3414 | 3413 | |
|
3415 | 3414 | This function lets you activate pylab (matplotlib, numpy and |
|
3416 | 3415 | interactive support) at any point during an IPython session. |
|
3417 | 3416 | |
|
3418 | 3417 | It will import at the top level numpy as np, pyplot as plt, matplotlib, |
|
3419 | 3418 | pylab and mlab, as well as all names from numpy and pylab. |
|
3420 | 3419 | |
|
3421 | 3420 | Parameters |
|
3422 | 3421 | ---------- |
|
3423 | 3422 | guiname : optional |
|
3424 | 3423 | One of the valid arguments to the %gui magic ('qt', 'wx', 'gtk', 'osx' or |
|
3425 | 3424 | 'tk'). If given, the corresponding Matplotlib backend is used, |
|
3426 | 3425 | otherwise matplotlib's default (which you can override in your |
|
3427 | 3426 | matplotlib config file) is used. |
|
3428 | 3427 | |
|
3429 | 3428 | Examples |
|
3430 | 3429 | -------- |
|
3431 | 3430 | In this case, where the MPL default is TkAgg: |
|
3432 | 3431 | In [2]: %pylab |
|
3433 | 3432 | |
|
3434 | 3433 | Welcome to pylab, a matplotlib-based Python environment. |
|
3435 | 3434 | Backend in use: TkAgg |
|
3436 | 3435 | For more information, type 'help(pylab)'. |
|
3437 | 3436 | |
|
3438 | 3437 | But you can explicitly request a different backend: |
|
3439 | 3438 | In [3]: %pylab qt |
|
3440 | 3439 | |
|
3441 | 3440 | Welcome to pylab, a matplotlib-based Python environment. |
|
3442 | 3441 | Backend in use: Qt4Agg |
|
3443 | 3442 | For more information, type 'help(pylab)'. |
|
3444 | 3443 | """ |
|
3445 | 3444 | self.shell.enable_pylab(s) |
|
3446 | 3445 | |
|
3447 | 3446 | def magic_tb(self, s): |
|
3448 | 3447 | """Print the last traceback with the currently active exception mode. |
|
3449 | 3448 | |
|
3450 | 3449 | See %xmode for changing exception reporting modes.""" |
|
3451 | 3450 | self.shell.showtraceback() |
|
3452 | 3451 | |
|
3453 | 3452 | @skip_doctest |
|
3454 | 3453 | def magic_precision(self, s=''): |
|
3455 | 3454 | """Set floating point precision for pretty printing. |
|
3456 | 3455 | |
|
3457 | 3456 | Can set either integer precision or a format string. |
|
3458 | 3457 | |
|
3459 | 3458 | If numpy has been imported and precision is an int, |
|
3460 | 3459 | numpy display precision will also be set, via ``numpy.set_printoptions``. |
|
3461 | 3460 | |
|
3462 | 3461 | If no argument is given, defaults will be restored. |
|
3463 | 3462 | |
|
3464 | 3463 | Examples |
|
3465 | 3464 | -------- |
|
3466 | 3465 | :: |
|
3467 | 3466 | |
|
3468 | 3467 | In [1]: from math import pi |
|
3469 | 3468 | |
|
3470 | 3469 | In [2]: %precision 3 |
|
3471 | 3470 | Out[2]: u'%.3f' |
|
3472 | 3471 | |
|
3473 | 3472 | In [3]: pi |
|
3474 | 3473 | Out[3]: 3.142 |
|
3475 | 3474 | |
|
3476 | 3475 | In [4]: %precision %i |
|
3477 | 3476 | Out[4]: u'%i' |
|
3478 | 3477 | |
|
3479 | 3478 | In [5]: pi |
|
3480 | 3479 | Out[5]: 3 |
|
3481 | 3480 | |
|
3482 | 3481 | In [6]: %precision %e |
|
3483 | 3482 | Out[6]: u'%e' |
|
3484 | 3483 | |
|
3485 | 3484 | In [7]: pi**10 |
|
3486 | 3485 | Out[7]: 9.364805e+04 |
|
3487 | 3486 | |
|
3488 | 3487 | In [8]: %precision |
|
3489 | 3488 | Out[8]: u'%r' |
|
3490 | 3489 | |
|
3491 | 3490 | In [9]: pi**10 |
|
3492 | 3491 | Out[9]: 93648.047476082982 |
|
3493 | 3492 | |
|
3494 | 3493 | """ |
|
3495 | 3494 | |
|
3496 | 3495 | ptformatter = self.shell.display_formatter.formatters['text/plain'] |
|
3497 | 3496 | ptformatter.float_precision = s |
|
3498 | 3497 | return ptformatter.float_format |
|
3499 | 3498 | |
|
3500 | 3499 | |
|
3501 | 3500 | @magic_arguments.magic_arguments() |
|
3502 | 3501 | @magic_arguments.argument( |
|
3503 | 3502 | '-e', '--export', action='store_true', default=False, |
|
3504 | 3503 | help='Export IPython history as a notebook. The filename argument ' |
|
3505 | 3504 | 'is used to specify the notebook name and format. For example ' |
|
3506 | 3505 | 'a filename of notebook.ipynb will result in a notebook name ' |
|
3507 | 3506 | 'of "notebook" and a format of "xml". Likewise using a ".json" ' |
|
3508 | 3507 | 'or ".py" file extension will write the notebook in the json ' |
|
3509 | 3508 | 'or py formats.' |
|
3510 | 3509 | ) |
|
3511 | 3510 | @magic_arguments.argument( |
|
3512 | 3511 | '-f', '--format', |
|
3513 | 3512 | help='Convert an existing IPython notebook to a new format. This option ' |
|
3514 | 3513 | 'specifies the new format and can have the values: xml, json, py. ' |
|
3515 | 3514 | 'The target filename is choosen automatically based on the new ' |
|
3516 | 3515 | 'format. The filename argument gives the name of the source file.' |
|
3517 | 3516 | ) |
|
3518 | 3517 | @magic_arguments.argument( |
|
3519 | 3518 | 'filename', type=unicode, |
|
3520 | 3519 | help='Notebook name or filename' |
|
3521 | 3520 | ) |
|
3522 | 3521 | def magic_notebook(self, s): |
|
3523 | 3522 | """Export and convert IPython notebooks. |
|
3524 | 3523 | |
|
3525 | 3524 | This function can export the current IPython history to a notebook file |
|
3526 | 3525 | or can convert an existing notebook file into a different format. For |
|
3527 | 3526 | example, to export the history to "foo.ipynb" do "%notebook -e foo.ipynb". |
|
3528 | 3527 | To export the history to "foo.py" do "%notebook -e foo.py". To convert |
|
3529 | 3528 | "foo.ipynb" to "foo.json" do "%notebook -f json foo.ipynb". Possible |
|
3530 | 3529 | formats include (xml/ipynb, json, py). |
|
3531 | 3530 | """ |
|
3532 | 3531 | args = magic_arguments.parse_argstring(self.magic_notebook, s) |
|
3533 | 3532 | |
|
3534 | 3533 | from IPython.nbformat import current |
|
3535 | 3534 | if args.export: |
|
3536 | 3535 | fname, name, format = current.parse_filename(args.filename) |
|
3537 | 3536 | cells = [] |
|
3538 | 3537 | hist = list(self.history_manager.get_range()) |
|
3539 | 3538 | for session, prompt_number, input in hist[:-1]: |
|
3540 | 3539 | cells.append(current.new_code_cell(prompt_number=prompt_number, input=input)) |
|
3541 | 3540 | worksheet = current.new_worksheet(cells=cells) |
|
3542 | 3541 | nb = current.new_notebook(name=name,worksheets=[worksheet]) |
|
3543 | 3542 | with open(fname, 'w') as f: |
|
3544 | 3543 | current.write(nb, f, format); |
|
3545 | 3544 | elif args.format is not None: |
|
3546 | 3545 | old_fname, old_name, old_format = current.parse_filename(args.filename) |
|
3547 | 3546 | new_format = args.format |
|
3548 | 3547 | if new_format == u'xml' or new_format == u'ipynb': |
|
3549 | 3548 | new_fname = old_name + u'.ipynb' |
|
3550 | 3549 | new_format = u'xml' |
|
3551 | 3550 | elif new_format == u'py': |
|
3552 | 3551 | new_fname = old_name + u'.py' |
|
3553 | 3552 | elif new_format == u'json': |
|
3554 | 3553 | new_fname = old_name + u'.json' |
|
3555 | 3554 | else: |
|
3556 | 3555 | raise ValueError('Invalid notebook format: %s' % newformat) |
|
3557 | 3556 | with open(old_fname, 'r') as f: |
|
3558 | 3557 | nb = current.read(f, old_format) |
|
3559 | 3558 | with open(new_fname, 'w') as f: |
|
3560 | 3559 | current.write(nb, f, new_format) |
|
3561 | 3560 | |
|
3562 | 3561 | |
|
3563 | 3562 | # end Magic |
@@ -1,249 +1,250 b'' | |||
|
1 | 1 | # encoding: utf-8 |
|
2 | 2 | """ |
|
3 | 3 | An application for managing IPython profiles. |
|
4 | 4 | |
|
5 | 5 | To be invoked as the `ipython profile` subcommand. |
|
6 | 6 | |
|
7 | 7 | Authors: |
|
8 | 8 | |
|
9 | 9 | * Min RK |
|
10 | 10 | |
|
11 | 11 | """ |
|
12 | 12 | |
|
13 | 13 | #----------------------------------------------------------------------------- |
|
14 | 14 | # Copyright (C) 2008-2011 The IPython Development Team |
|
15 | 15 | # |
|
16 | 16 | # Distributed under the terms of the BSD License. The full license is in |
|
17 | 17 | # the file COPYING, distributed as part of this software. |
|
18 | 18 | #----------------------------------------------------------------------------- |
|
19 | 19 | |
|
20 | 20 | #----------------------------------------------------------------------------- |
|
21 | 21 | # Imports |
|
22 | 22 | #----------------------------------------------------------------------------- |
|
23 | 23 | |
|
24 | 24 | import logging |
|
25 | 25 | import os |
|
26 | 26 | |
|
27 | 27 | from IPython.config.application import Application, boolean_flag |
|
28 | 28 | from IPython.core.application import ( |
|
29 | 29 | BaseIPythonApplication, base_flags, base_aliases |
|
30 | 30 | ) |
|
31 | 31 | from IPython.core.profiledir import ProfileDir |
|
32 | 32 | from IPython.utils.path import get_ipython_dir |
|
33 | 33 | from IPython.utils.traitlets import Unicode, Bool, Dict |
|
34 | 34 | |
|
35 | 35 | #----------------------------------------------------------------------------- |
|
36 | 36 | # Constants |
|
37 | 37 | #----------------------------------------------------------------------------- |
|
38 | 38 | |
|
39 | 39 | create_help = """Create an IPython profile by name |
|
40 | 40 | |
|
41 | 41 | Create an ipython profile directory by its name or |
|
42 | 42 | profile directory path. Profile directories contain |
|
43 | 43 | configuration, log and security related files and are named |
|
44 | 44 | using the convention 'profile_<name>'. By default they are |
|
45 | 45 | located in your ipython directory. Once created, you will |
|
46 | 46 | can edit the configuration files in the profile |
|
47 | 47 | directory to configure IPython. Most users will create a |
|
48 | 48 | profile directory by name, |
|
49 | 49 | `ipython profile create myprofile`, which will put the directory |
|
50 | 50 | in `<ipython_dir>/profile_myprofile`. |
|
51 | 51 | """ |
|
52 | 52 | list_help = """List available IPython profiles |
|
53 | 53 | |
|
54 | 54 | List all available profiles, by profile location, that can |
|
55 | 55 | be found in the current working directly or in the ipython |
|
56 | 56 | directory. Profile directories are named using the convention |
|
57 | 57 | 'profile_<profile>'. |
|
58 | 58 | """ |
|
59 | 59 | profile_help = """Manage IPython profiles |
|
60 | 60 | |
|
61 | 61 | Profile directories contain |
|
62 | 62 | configuration, log and security related files and are named |
|
63 | 63 | using the convention 'profile_<name>'. By default they are |
|
64 | 64 | located in your ipython directory. You can create profiles |
|
65 | 65 | with `ipython profile create <name>`, or see the profiles you |
|
66 | 66 | already have with `ipython profile list` |
|
67 | 67 | |
|
68 | 68 | To get started configuring IPython, simply do: |
|
69 | 69 | |
|
70 | 70 | $> ipython profile create |
|
71 | 71 | |
|
72 | 72 | and IPython will create the default profile in <ipython_dir>/profile_default, |
|
73 | 73 | where you can edit ipython_config.py to start configuring IPython. |
|
74 | 74 | |
|
75 | 75 | """ |
|
76 | 76 | |
|
77 | 77 | _list_examples = "ipython profile list # list all profiles" |
|
78 | 78 | |
|
79 | 79 | _create_examples = """ |
|
80 | 80 | ipython profile create foo # create profile foo w/ default config files |
|
81 | 81 | ipython profile create foo --reset # restage default config files over current |
|
82 | 82 | ipython profile create foo --parallel # also stage parallel config files |
|
83 | 83 | """ |
|
84 | 84 | |
|
85 | 85 | _main_examples = """ |
|
86 | 86 | ipython profile create -h # show the help string for the create subcommand |
|
87 | 87 | ipython profile list -h # show the help string for the list subcommand |
|
88 | 88 | """ |
|
89 | 89 | |
|
90 | 90 | #----------------------------------------------------------------------------- |
|
91 | 91 | # Profile Application Class (for `ipython profile` subcommand) |
|
92 | 92 | #----------------------------------------------------------------------------- |
|
93 | 93 | |
|
94 | 94 | |
|
95 | 95 | class ProfileList(Application): |
|
96 | 96 | name = u'ipython-profile' |
|
97 | 97 | description = list_help |
|
98 | 98 | examples = _list_examples |
|
99 | 99 | |
|
100 | 100 | aliases = Dict({ |
|
101 | 101 | 'ipython-dir' : 'ProfileList.ipython_dir', |
|
102 | 102 | 'log-level' : 'Application.log_level', |
|
103 | 103 | }) |
|
104 | 104 | flags = Dict(dict( |
|
105 | 105 | debug = ({'Application' : {'log_level' : 0}}, |
|
106 | 106 | "Set Application.log_level to 0, maximizing log output." |
|
107 | 107 | ) |
|
108 | 108 | )) |
|
109 | 109 | |
|
110 | 110 | ipython_dir = Unicode(get_ipython_dir(), config=True, |
|
111 | 111 | help=""" |
|
112 | 112 | The name of the IPython directory. This directory is used for logging |
|
113 | 113 | configuration (through profiles), history storage, etc. The default |
|
114 | 114 | is usually $HOME/.ipython. This options can also be specified through |
|
115 | 115 | the environment variable IPYTHON_DIR. |
|
116 | 116 | """ |
|
117 | 117 | ) |
|
118 | 118 | |
|
119 | 119 | def list_profile_dirs(self): |
|
120 | 120 | # Find the search paths |
|
121 | 121 | paths = [os.getcwdu(), self.ipython_dir] |
|
122 | 122 | |
|
123 | 123 | self.log.warn('Searching for IPython profiles in paths: %r' % paths) |
|
124 | 124 | for path in paths: |
|
125 | 125 | files = os.listdir(path) |
|
126 | 126 | for f in files: |
|
127 | 127 | full_path = os.path.join(path, f) |
|
128 | 128 | if os.path.isdir(full_path) and f.startswith('profile_'): |
|
129 | 129 | profile = f.split('_',1)[-1] |
|
130 | 130 | start_cmd = 'ipython profile=%s' % profile |
|
131 | 131 | print start_cmd + " ==> " + full_path |
|
132 | 132 | |
|
133 | 133 | def start(self): |
|
134 | 134 | self.list_profile_dirs() |
|
135 | 135 | |
|
136 | 136 | |
|
137 | 137 | create_flags = {} |
|
138 | 138 | create_flags.update(base_flags) |
|
139 | 139 | # don't include '--init' flag, which implies running profile create in other apps |
|
140 | 140 | create_flags.pop('init') |
|
141 | 141 | create_flags['reset'] = ({'ProfileCreate': {'overwrite' : True}}, |
|
142 | 142 | "reset config files in this profile to the defaults.") |
|
143 | 143 | create_flags['parallel'] = ({'ProfileCreate': {'parallel' : True}}, |
|
144 | 144 | "Include the config files for parallel " |
|
145 | 145 | "computing apps (ipengine, ipcontroller, etc.)") |
|
146 | 146 | |
|
147 | 147 | |
|
148 | 148 | class ProfileCreate(BaseIPythonApplication): |
|
149 | 149 | name = u'ipython-profile' |
|
150 | 150 | description = create_help |
|
151 | 151 | examples = _create_examples |
|
152 | 152 | auto_create = Bool(True, config=False) |
|
153 | 153 | |
|
154 | 154 | def _copy_config_files_default(self): |
|
155 | 155 | return True |
|
156 | 156 | |
|
157 | 157 | parallel = Bool(False, config=True, |
|
158 | 158 | help="whether to include parallel computing config files") |
|
159 | 159 | def _parallel_changed(self, name, old, new): |
|
160 | 160 | parallel_files = [ 'ipcontroller_config.py', |
|
161 | 161 | 'ipengine_config.py', |
|
162 | 162 | 'ipcluster_config.py' |
|
163 | 163 | ] |
|
164 | 164 | if new: |
|
165 | 165 | for cf in parallel_files: |
|
166 | 166 | self.config_files.append(cf) |
|
167 | 167 | else: |
|
168 | 168 | for cf in parallel_files: |
|
169 | 169 | if cf in self.config_files: |
|
170 | 170 | self.config_files.remove(cf) |
|
171 | 171 | |
|
172 | 172 | def parse_command_line(self, argv): |
|
173 | 173 | super(ProfileCreate, self).parse_command_line(argv) |
|
174 | 174 | # accept positional arg as profile name |
|
175 | 175 | if self.extra_args: |
|
176 | 176 | self.profile = self.extra_args[0] |
|
177 | 177 | |
|
178 | 178 | flags = Dict(create_flags) |
|
179 | 179 | |
|
180 | 180 | classes = [ProfileDir] |
|
181 | 181 | |
|
182 | 182 | def init_config_files(self): |
|
183 | 183 | super(ProfileCreate, self).init_config_files() |
|
184 | 184 | # use local imports, since these classes may import from here |
|
185 | 185 | from IPython.frontend.terminal.ipapp import TerminalIPythonApp |
|
186 | 186 | apps = [TerminalIPythonApp] |
|
187 | 187 | try: |
|
188 | 188 | from IPython.frontend.qt.console.qtconsoleapp import IPythonQtConsoleApp |
|
189 | 189 | except Exception: |
|
190 | 190 | # this should be ImportError, but under weird circumstances |
|
191 | 191 | # this might be an AttributeError, or possibly others |
|
192 | 192 | # in any case, nothing should cause the profile creation to crash. |
|
193 | 193 | pass |
|
194 | 194 | else: |
|
195 | 195 | apps.append(IPythonQtConsoleApp) |
|
196 | 196 | try: |
|
197 | 197 | from IPython.frontend.html.notebook.notebookapp import IPythonNotebookApp |
|
198 |
except |
|
|
199 | # this should be ImportError, but under weird circumstances | |
|
200 | # this might be an AttributeError, or possibly others | |
|
201 | # in any case, nothing should cause the profile creation to crash. | |
|
198 | except ImportError: | |
|
202 | 199 | pass |
|
200 | except Exception: | |
|
201 | self.log.debug('Unexpected error when importing IPythonNotebookApp', | |
|
202 | exc_info=True | |
|
203 | ) | |
|
203 | 204 | else: |
|
204 | 205 | apps.append(IPythonNotebookApp) |
|
205 | 206 | if self.parallel: |
|
206 | 207 | from IPython.parallel.apps.ipcontrollerapp import IPControllerApp |
|
207 | 208 | from IPython.parallel.apps.ipengineapp import IPEngineApp |
|
208 | 209 | from IPython.parallel.apps.ipclusterapp import IPClusterStart |
|
209 | 210 | from IPython.parallel.apps.iploggerapp import IPLoggerApp |
|
210 | 211 | apps.extend([ |
|
211 | 212 | IPControllerApp, |
|
212 | 213 | IPEngineApp, |
|
213 | 214 | IPClusterStart, |
|
214 | 215 | IPLoggerApp, |
|
215 | 216 | ]) |
|
216 | 217 | for App in apps: |
|
217 | 218 | app = App() |
|
218 | 219 | app.config.update(self.config) |
|
219 | 220 | app.log = self.log |
|
220 | 221 | app.overwrite = self.overwrite |
|
221 | 222 | app.copy_config_files=True |
|
222 | 223 | app.profile = self.profile |
|
223 | 224 | app.init_profile_dir() |
|
224 | 225 | app.init_config_files() |
|
225 | 226 | |
|
226 | 227 | def stage_default_config_file(self): |
|
227 | 228 | pass |
|
228 | 229 | |
|
229 | 230 | |
|
230 | 231 | class ProfileApp(Application): |
|
231 | 232 | name = u'ipython-profile' |
|
232 | 233 | description = profile_help |
|
233 | 234 | examples = _main_examples |
|
234 | 235 | |
|
235 | 236 | subcommands = Dict(dict( |
|
236 | 237 | create = (ProfileCreate, "Create a new profile dir with default config files"), |
|
237 | 238 | list = (ProfileList, "List existing profiles") |
|
238 | 239 | )) |
|
239 | 240 | |
|
240 | 241 | def start(self): |
|
241 | 242 | if self.subapp is None: |
|
242 | 243 | print "No subcommand specified. Must specify one of: %s"%(self.subcommands.keys()) |
|
243 | 244 | |
|
244 | 245 | self.print_description() |
|
245 | 246 | self.print_subcommands() |
|
246 | 247 | self.exit(1) |
|
247 | 248 | else: |
|
248 | 249 | return self.subapp.start() |
|
249 | 250 |
@@ -1,326 +1,337 b'' | |||
|
1 |
"""Tornado handlers for the notebook. |
|
|
1 | """Tornado handlers for the notebook. | |
|
2 | ||
|
3 | Authors: | |
|
4 | ||
|
5 | * Brian Granger | |
|
6 | """ | |
|
2 | 7 | |
|
3 | 8 | #----------------------------------------------------------------------------- |
|
4 | # Imports | |
|
9 | # Copyright (C) 2008-2011 The IPython Development Team | |
|
10 | # | |
|
11 | # Distributed under the terms of the BSD License. The full license is in | |
|
12 | # the file COPYING, distributed as part of this software. | |
|
5 | 13 | #----------------------------------------------------------------------------- |
|
6 | 14 | |
|
15 | #----------------------------------------------------------------------------- | |
|
16 | # Imports | |
|
17 | #----------------------------------------------------------------------------- | |
|
7 | 18 | |
|
8 | 19 | from tornado import web |
|
9 | 20 | from tornado import websocket |
|
10 | 21 | |
|
11 | 22 | from zmq.eventloop import ioloop |
|
12 | 23 | from zmq.utils import jsonapi |
|
13 | 24 | |
|
14 | 25 | from IPython.zmq.session import Session |
|
15 | 26 | |
|
16 | 27 | try: |
|
17 | 28 | from docutils.core import publish_string |
|
18 | 29 | except ImportError: |
|
19 | 30 | publish_string = None |
|
20 | 31 | |
|
21 | 32 | |
|
22 | 33 | |
|
23 | 34 | #----------------------------------------------------------------------------- |
|
24 | 35 | # Top-level handlers |
|
25 | 36 | #----------------------------------------------------------------------------- |
|
26 | 37 | |
|
27 | 38 | |
|
28 | 39 | class NBBrowserHandler(web.RequestHandler): |
|
29 | 40 | def get(self): |
|
30 | 41 | nbm = self.application.notebook_manager |
|
31 | 42 | project = nbm.notebook_dir |
|
32 | 43 | self.render('nbbrowser.html', project=project) |
|
33 | 44 | |
|
34 | 45 | |
|
35 | 46 | class NewHandler(web.RequestHandler): |
|
36 | 47 | def get(self): |
|
37 | 48 | notebook_id = self.application.notebook_manager.new_notebook() |
|
38 | 49 | self.render('notebook.html', notebook_id=notebook_id) |
|
39 | 50 | |
|
40 | 51 | |
|
41 | 52 | class NamedNotebookHandler(web.RequestHandler): |
|
42 | 53 | def get(self, notebook_id): |
|
43 | 54 | nbm = self.application.notebook_manager |
|
44 | 55 | if not nbm.notebook_exists(notebook_id): |
|
45 | 56 | raise web.HTTPError(404) |
|
46 | 57 | self.render('notebook.html', notebook_id=notebook_id) |
|
47 | 58 | |
|
48 | 59 | |
|
49 | 60 | #----------------------------------------------------------------------------- |
|
50 | 61 | # Kernel handlers |
|
51 | 62 | #----------------------------------------------------------------------------- |
|
52 | 63 | |
|
53 | 64 | |
|
54 | 65 | class MainKernelHandler(web.RequestHandler): |
|
55 | 66 | |
|
56 | 67 | def get(self): |
|
57 | 68 | km = self.application.kernel_manager |
|
58 | 69 | self.finish(jsonapi.dumps(km.kernel_ids)) |
|
59 | 70 | |
|
60 | 71 | def post(self): |
|
61 | 72 | km = self.application.kernel_manager |
|
62 | 73 | notebook_id = self.get_argument('notebook', default=None) |
|
63 | 74 | kernel_id = km.start_kernel(notebook_id) |
|
64 | 75 | ws_url = self.application.ipython_app.get_ws_url() |
|
65 | 76 | data = {'ws_url':ws_url,'kernel_id':kernel_id} |
|
66 | 77 | self.set_header('Location', '/'+kernel_id) |
|
67 | 78 | self.finish(jsonapi.dumps(data)) |
|
68 | 79 | |
|
69 | 80 | |
|
70 | 81 | class KernelHandler(web.RequestHandler): |
|
71 | 82 | |
|
72 | 83 | SUPPORTED_METHODS = ('DELETE') |
|
73 | 84 | |
|
74 | 85 | def delete(self, kernel_id): |
|
75 | 86 | km = self.application.kernel_manager |
|
76 | 87 | km.kill_kernel(kernel_id) |
|
77 | 88 | self.set_status(204) |
|
78 | 89 | self.finish() |
|
79 | 90 | |
|
80 | 91 | |
|
81 | 92 | class KernelActionHandler(web.RequestHandler): |
|
82 | 93 | |
|
83 | 94 | def post(self, kernel_id, action): |
|
84 | 95 | km = self.application.kernel_manager |
|
85 | 96 | if action == 'interrupt': |
|
86 | 97 | km.interrupt_kernel(kernel_id) |
|
87 | 98 | self.set_status(204) |
|
88 | 99 | if action == 'restart': |
|
89 | 100 | new_kernel_id = km.restart_kernel(kernel_id) |
|
90 | 101 | ws_url = self.application.ipython_app.get_ws_url() |
|
91 | 102 | data = {'ws_url':ws_url,'kernel_id':new_kernel_id} |
|
92 | 103 | self.set_header('Location', '/'+new_kernel_id) |
|
93 | 104 | self.write(jsonapi.dumps(data)) |
|
94 | 105 | self.finish() |
|
95 | 106 | |
|
96 | 107 | |
|
97 | 108 | class ZMQStreamHandler(websocket.WebSocketHandler): |
|
98 | 109 | |
|
99 | 110 | def _reserialize_reply(self, msg_list): |
|
100 | 111 | """Reserialize a reply message using JSON. |
|
101 | 112 | |
|
102 | 113 | This takes the msg list from the ZMQ socket, unserializes it using |
|
103 | 114 | self.session and then serializes the result using JSON. This method |
|
104 | 115 | should be used by self._on_zmq_reply to build messages that can |
|
105 | 116 | be sent back to the browser. |
|
106 | 117 | """ |
|
107 | 118 | idents, msg_list = self.session.feed_identities(msg_list) |
|
108 | 119 | msg = self.session.unserialize(msg_list) |
|
109 | 120 | try: |
|
110 | 121 | msg['header'].pop('date') |
|
111 | 122 | except KeyError: |
|
112 | 123 | pass |
|
113 | 124 | try: |
|
114 | 125 | msg['parent_header'].pop('date') |
|
115 | 126 | except KeyError: |
|
116 | 127 | pass |
|
117 | 128 | msg.pop('buffers') |
|
118 | 129 | return jsonapi.dumps(msg) |
|
119 | 130 | |
|
120 | 131 | def _on_zmq_reply(self, msg_list): |
|
121 | 132 | try: |
|
122 | 133 | msg = self._reserialize_reply(msg_list) |
|
123 | 134 | except: |
|
124 | 135 | self.application.kernel_manager.log.critical("Malformed message: %r" % msg_list) |
|
125 | 136 | else: |
|
126 | 137 | self.write_message(msg) |
|
127 | 138 | |
|
128 | 139 | |
|
129 | 140 | class IOPubHandler(ZMQStreamHandler): |
|
130 | 141 | |
|
131 | 142 | def initialize(self, *args, **kwargs): |
|
132 | 143 | self._kernel_alive = True |
|
133 | 144 | self._beating = False |
|
134 | 145 | self.iopub_stream = None |
|
135 | 146 | self.hb_stream = None |
|
136 | 147 | |
|
137 | 148 | def open(self, kernel_id): |
|
138 | 149 | km = self.application.kernel_manager |
|
139 | 150 | self.kernel_id = kernel_id |
|
140 | 151 | self.session = Session() |
|
141 | 152 | self.time_to_dead = km.time_to_dead |
|
142 | 153 | try: |
|
143 | 154 | self.iopub_stream = km.create_iopub_stream(kernel_id) |
|
144 | 155 | self.hb_stream = km.create_hb_stream(kernel_id) |
|
145 | 156 | except web.HTTPError: |
|
146 | 157 | # WebSockets don't response to traditional error codes so we |
|
147 | 158 | # close the connection. |
|
148 | 159 | if not self.stream.closed(): |
|
149 | 160 | self.stream.close() |
|
150 | 161 | else: |
|
151 | 162 | self.iopub_stream.on_recv(self._on_zmq_reply) |
|
152 | 163 | self.start_hb(self.kernel_died) |
|
153 | 164 | |
|
154 | 165 | def on_close(self): |
|
155 | 166 | # This method can be called twice, once by self.kernel_died and once |
|
156 | 167 | # from the WebSocket close event. If the WebSocket connection is |
|
157 | 168 | # closed before the ZMQ streams are setup, they could be None. |
|
158 | 169 | self.stop_hb() |
|
159 | 170 | if self.iopub_stream is not None and not self.iopub_stream.closed(): |
|
160 | 171 | self.iopub_stream.on_recv(None) |
|
161 | 172 | self.iopub_stream.close() |
|
162 | 173 | if self.hb_stream is not None and not self.hb_stream.closed(): |
|
163 | 174 | self.hb_stream.close() |
|
164 | 175 | |
|
165 | 176 | def start_hb(self, callback): |
|
166 | 177 | """Start the heartbeating and call the callback if the kernel dies.""" |
|
167 | 178 | if not self._beating: |
|
168 | 179 | self._kernel_alive = True |
|
169 | 180 | |
|
170 | 181 | def ping_or_dead(): |
|
171 | 182 | if self._kernel_alive: |
|
172 | 183 | self._kernel_alive = False |
|
173 | 184 | self.hb_stream.send(b'ping') |
|
174 | 185 | else: |
|
175 | 186 | try: |
|
176 | 187 | callback() |
|
177 | 188 | except: |
|
178 | 189 | pass |
|
179 | 190 | finally: |
|
180 | 191 | self._hb_periodic_callback.stop() |
|
181 | 192 | |
|
182 | 193 | def beat_received(msg): |
|
183 | 194 | self._kernel_alive = True |
|
184 | 195 | |
|
185 | 196 | self.hb_stream.on_recv(beat_received) |
|
186 | 197 | self._hb_periodic_callback = ioloop.PeriodicCallback(ping_or_dead, self.time_to_dead*1000) |
|
187 | 198 | self._hb_periodic_callback.start() |
|
188 | 199 | self._beating= True |
|
189 | 200 | |
|
190 | 201 | def stop_hb(self): |
|
191 | 202 | """Stop the heartbeating and cancel all related callbacks.""" |
|
192 | 203 | if self._beating: |
|
193 | 204 | self._hb_periodic_callback.stop() |
|
194 | 205 | if not self.hb_stream.closed(): |
|
195 | 206 | self.hb_stream.on_recv(None) |
|
196 | 207 | |
|
197 | 208 | def kernel_died(self): |
|
198 | 209 | self.application.kernel_manager.delete_mapping_for_kernel(self.kernel_id) |
|
199 | 210 | self.write_message( |
|
200 | 211 | {'header': {'msg_type': 'status'}, |
|
201 | 212 | 'parent_header': {}, |
|
202 | 213 | 'content': {'execution_state':'dead'} |
|
203 | 214 | } |
|
204 | 215 | ) |
|
205 | 216 | self.on_close() |
|
206 | 217 | |
|
207 | 218 | |
|
208 | 219 | class ShellHandler(ZMQStreamHandler): |
|
209 | 220 | |
|
210 | 221 | def initialize(self, *args, **kwargs): |
|
211 | 222 | self.shell_stream = None |
|
212 | 223 | |
|
213 | 224 | def open(self, kernel_id): |
|
214 | 225 | km = self.application.kernel_manager |
|
215 | 226 | self.max_msg_size = km.max_msg_size |
|
216 | 227 | self.kernel_id = kernel_id |
|
217 | 228 | try: |
|
218 | 229 | self.shell_stream = km.create_shell_stream(kernel_id) |
|
219 | 230 | except web.HTTPError: |
|
220 | 231 | # WebSockets don't response to traditional error codes so we |
|
221 | 232 | # close the connection. |
|
222 | 233 | if not self.stream.closed(): |
|
223 | 234 | self.stream.close() |
|
224 | 235 | else: |
|
225 | 236 | self.session = Session() |
|
226 | 237 | self.shell_stream.on_recv(self._on_zmq_reply) |
|
227 | 238 | |
|
228 | 239 | def on_message(self, msg): |
|
229 | 240 | if len(msg) < self.max_msg_size: |
|
230 | 241 | msg = jsonapi.loads(msg) |
|
231 | 242 | self.session.send(self.shell_stream, msg) |
|
232 | 243 | |
|
233 | 244 | def on_close(self): |
|
234 | 245 | # Make sure the stream exists and is not already closed. |
|
235 | 246 | if self.shell_stream is not None and not self.shell_stream.closed(): |
|
236 | 247 | self.shell_stream.close() |
|
237 | 248 | |
|
238 | 249 | |
|
239 | 250 | #----------------------------------------------------------------------------- |
|
240 | 251 | # Notebook web service handlers |
|
241 | 252 | #----------------------------------------------------------------------------- |
|
242 | 253 | |
|
243 | 254 | class NotebookRootHandler(web.RequestHandler): |
|
244 | 255 | |
|
245 | 256 | def get(self): |
|
246 | 257 | nbm = self.application.notebook_manager |
|
247 | 258 | files = nbm.list_notebooks() |
|
248 | 259 | self.finish(jsonapi.dumps(files)) |
|
249 | 260 | |
|
250 | 261 | def post(self): |
|
251 | 262 | nbm = self.application.notebook_manager |
|
252 | 263 | body = self.request.body.strip() |
|
253 | 264 | format = self.get_argument('format', default='json') |
|
254 | 265 | name = self.get_argument('name', default=None) |
|
255 | 266 | if body: |
|
256 | 267 | notebook_id = nbm.save_new_notebook(body, name=name, format=format) |
|
257 | 268 | else: |
|
258 | 269 | notebook_id = nbm.new_notebook() |
|
259 | 270 | self.set_header('Location', '/'+notebook_id) |
|
260 | 271 | self.finish(jsonapi.dumps(notebook_id)) |
|
261 | 272 | |
|
262 | 273 | |
|
263 | 274 | class NotebookHandler(web.RequestHandler): |
|
264 | 275 | |
|
265 | 276 | SUPPORTED_METHODS = ('GET', 'PUT', 'DELETE') |
|
266 | 277 | |
|
267 | 278 | def get(self, notebook_id): |
|
268 | 279 | nbm = self.application.notebook_manager |
|
269 | 280 | format = self.get_argument('format', default='json') |
|
270 | 281 | last_mod, name, data = nbm.get_notebook(notebook_id, format) |
|
271 | 282 | if format == u'json': |
|
272 | 283 | self.set_header('Content-Type', 'application/json') |
|
273 | 284 | self.set_header('Content-Disposition','attachment; filename="%s.json"' % name) |
|
274 | 285 | elif format == u'xml': |
|
275 | 286 | self.set_header('Content-Type', 'application/xml') |
|
276 | 287 | self.set_header('Content-Disposition','attachment; filename="%s.ipynb"' % name) |
|
277 | 288 | elif format == u'py': |
|
278 | 289 | self.set_header('Content-Type', 'application/x-python') |
|
279 | 290 | self.set_header('Content-Disposition','attachment; filename="%s.py"' % name) |
|
280 | 291 | self.set_header('Last-Modified', last_mod) |
|
281 | 292 | self.finish(data) |
|
282 | 293 | |
|
283 | 294 | def put(self, notebook_id): |
|
284 | 295 | nbm = self.application.notebook_manager |
|
285 | 296 | format = self.get_argument('format', default='json') |
|
286 | 297 | name = self.get_argument('name', default=None) |
|
287 | 298 | nbm.save_notebook(notebook_id, self.request.body, name=name, format=format) |
|
288 | 299 | self.set_status(204) |
|
289 | 300 | self.finish() |
|
290 | 301 | |
|
291 | 302 | def delete(self, notebook_id): |
|
292 | 303 | nbm = self.application.notebook_manager |
|
293 | 304 | nbm.delete_notebook(notebook_id) |
|
294 | 305 | self.set_status(204) |
|
295 | 306 | self.finish() |
|
296 | 307 | |
|
297 | 308 | #----------------------------------------------------------------------------- |
|
298 | 309 | # RST web service handlers |
|
299 | 310 | #----------------------------------------------------------------------------- |
|
300 | 311 | |
|
301 | 312 | |
|
302 | 313 | class RSTHandler(web.RequestHandler): |
|
303 | 314 | |
|
304 | 315 | def post(self): |
|
305 | 316 | if publish_string is None: |
|
306 | 317 | raise web.HTTPError(503) |
|
307 | 318 | body = self.request.body.strip() |
|
308 | 319 | source = body |
|
309 | 320 | # template_path=os.path.join(os.path.dirname(__file__), u'templates', u'rst_template.html') |
|
310 | 321 | defaults = {'file_insertion_enabled': 0, |
|
311 | 322 | 'raw_enabled': 0, |
|
312 | 323 | '_disable_config': 1, |
|
313 | 324 | 'stylesheet_path': 0 |
|
314 | 325 | # 'template': template_path |
|
315 | 326 | } |
|
316 | 327 | try: |
|
317 | 328 | html = publish_string(source, writer_name='html', |
|
318 | 329 | settings_overrides=defaults |
|
319 | 330 | ) |
|
320 | 331 | except: |
|
321 | 332 | raise web.HTTPError(400) |
|
322 | 333 | print html |
|
323 | 334 | self.set_header('Content-Type', 'text/html') |
|
324 | 335 | self.finish(html) |
|
325 | 336 | |
|
326 | 337 |
@@ -1,320 +1,325 b'' | |||
|
1 |
"""A kernel manager for multiple kernels. |
|
|
1 | """A kernel manager for multiple kernels. | |
|
2 | ||
|
3 | Authors: | |
|
4 | ||
|
5 | * Brian Granger | |
|
6 | """ | |
|
2 | 7 | |
|
3 | 8 | #----------------------------------------------------------------------------- |
|
4 | # Copyright (C) 2011 The IPython Development Team | |
|
9 | # Copyright (C) 2008-2011 The IPython Development Team | |
|
5 | 10 | # |
|
6 | 11 | # Distributed under the terms of the BSD License. The full license is in |
|
7 |
# the file COPYING |
|
|
12 | # the file COPYING, distributed as part of this software. | |
|
8 | 13 | #----------------------------------------------------------------------------- |
|
9 | 14 | |
|
10 | 15 | #----------------------------------------------------------------------------- |
|
11 | 16 | # Imports |
|
12 | 17 | #----------------------------------------------------------------------------- |
|
13 | 18 | |
|
14 | 19 | import signal |
|
15 | 20 | import sys |
|
16 | 21 | import uuid |
|
17 | 22 | |
|
18 | 23 | import zmq |
|
19 | 24 | from zmq.eventloop.zmqstream import ZMQStream |
|
20 | 25 | |
|
21 | 26 | from tornado import web |
|
22 | 27 | |
|
23 | 28 | from IPython.config.configurable import LoggingConfigurable |
|
24 | 29 | from IPython.zmq.ipkernel import launch_kernel |
|
25 | 30 | from IPython.utils.traitlets import Instance, Dict, List, Unicode, Float, Int |
|
26 | 31 | |
|
27 | 32 | #----------------------------------------------------------------------------- |
|
28 | 33 | # Classes |
|
29 | 34 | #----------------------------------------------------------------------------- |
|
30 | 35 | |
|
31 | 36 | class DuplicateKernelError(Exception): |
|
32 | 37 | pass |
|
33 | 38 | |
|
34 | 39 | |
|
35 | 40 | class KernelManager(LoggingConfigurable): |
|
36 | 41 | """A class for managing multiple kernels.""" |
|
37 | 42 | |
|
38 | 43 | context = Instance('zmq.Context') |
|
39 | 44 | def _context_default(self): |
|
40 | 45 | return zmq.Context.instance() |
|
41 | 46 | |
|
42 | 47 | _kernels = Dict() |
|
43 | 48 | |
|
44 | 49 | @property |
|
45 | 50 | def kernel_ids(self): |
|
46 | 51 | """Return a list of the kernel ids of the active kernels.""" |
|
47 | 52 | return self._kernels.keys() |
|
48 | 53 | |
|
49 | 54 | def __len__(self): |
|
50 | 55 | """Return the number of running kernels.""" |
|
51 | 56 | return len(self.kernel_ids) |
|
52 | 57 | |
|
53 | 58 | def __contains__(self, kernel_id): |
|
54 | 59 | if kernel_id in self.kernel_ids: |
|
55 | 60 | return True |
|
56 | 61 | else: |
|
57 | 62 | return False |
|
58 | 63 | |
|
59 | 64 | def start_kernel(self, **kwargs): |
|
60 | 65 | """Start a new kernel.""" |
|
61 | 66 | kernel_id = unicode(uuid.uuid4()) |
|
62 | 67 | (process, shell_port, iopub_port, stdin_port, hb_port) = launch_kernel(**kwargs) |
|
63 | 68 | # Store the information for contacting the kernel. This assumes the kernel is |
|
64 | 69 | # running on localhost. |
|
65 | 70 | d = dict( |
|
66 | 71 | process = process, |
|
67 | 72 | stdin_port = stdin_port, |
|
68 | 73 | iopub_port = iopub_port, |
|
69 | 74 | shell_port = shell_port, |
|
70 | 75 | hb_port = hb_port, |
|
71 | 76 | ip = '127.0.0.1' |
|
72 | 77 | ) |
|
73 | 78 | self._kernels[kernel_id] = d |
|
74 | 79 | return kernel_id |
|
75 | 80 | |
|
76 | 81 | def kill_kernel(self, kernel_id): |
|
77 | 82 | """Kill a kernel by its kernel uuid. |
|
78 | 83 | |
|
79 | 84 | Parameters |
|
80 | 85 | ========== |
|
81 | 86 | kernel_id : uuid |
|
82 | 87 | The id of the kernel to kill. |
|
83 | 88 | """ |
|
84 | 89 | kernel_process = self.get_kernel_process(kernel_id) |
|
85 | 90 | if kernel_process is not None: |
|
86 | 91 | # Attempt to kill the kernel. |
|
87 | 92 | try: |
|
88 | 93 | kernel_process.kill() |
|
89 | 94 | except OSError, e: |
|
90 | 95 | # In Windows, we will get an Access Denied error if the process |
|
91 | 96 | # has already terminated. Ignore it. |
|
92 | 97 | if not (sys.platform == 'win32' and e.winerror == 5): |
|
93 | 98 | raise |
|
94 | 99 | del self._kernels[kernel_id] |
|
95 | 100 | |
|
96 | 101 | def interrupt_kernel(self, kernel_id): |
|
97 | 102 | """Interrupt (SIGINT) the kernel by its uuid. |
|
98 | 103 | |
|
99 | 104 | Parameters |
|
100 | 105 | ========== |
|
101 | 106 | kernel_id : uuid |
|
102 | 107 | The id of the kernel to interrupt. |
|
103 | 108 | """ |
|
104 | 109 | kernel_process = self.get_kernel_process(kernel_id) |
|
105 | 110 | if kernel_process is not None: |
|
106 | 111 | if sys.platform == 'win32': |
|
107 | 112 | from parentpoller import ParentPollerWindows as Poller |
|
108 | 113 | Poller.send_interrupt(kernel_process.win32_interrupt_event) |
|
109 | 114 | else: |
|
110 | 115 | kernel_process.send_signal(signal.SIGINT) |
|
111 | 116 | |
|
112 | 117 | |
|
113 | 118 | def signal_kernel(self, kernel_id, signum): |
|
114 | 119 | """ Sends a signal to the kernel by its uuid. |
|
115 | 120 | |
|
116 | 121 | Note that since only SIGTERM is supported on Windows, this function |
|
117 | 122 | is only useful on Unix systems. |
|
118 | 123 | |
|
119 | 124 | Parameters |
|
120 | 125 | ========== |
|
121 | 126 | kernel_id : uuid |
|
122 | 127 | The id of the kernel to signal. |
|
123 | 128 | """ |
|
124 | 129 | kernel_process = self.get_kernel_process(kernel_id) |
|
125 | 130 | if kernel_process is not None: |
|
126 | 131 | kernel_process.send_signal(signum) |
|
127 | 132 | |
|
128 | 133 | def get_kernel_process(self, kernel_id): |
|
129 | 134 | """Get the process object for a kernel by its uuid. |
|
130 | 135 | |
|
131 | 136 | Parameters |
|
132 | 137 | ========== |
|
133 | 138 | kernel_id : uuid |
|
134 | 139 | The id of the kernel. |
|
135 | 140 | """ |
|
136 | 141 | d = self._kernels.get(kernel_id) |
|
137 | 142 | if d is not None: |
|
138 | 143 | return d['process'] |
|
139 | 144 | else: |
|
140 | 145 | raise KeyError("Kernel with id not found: %s" % kernel_id) |
|
141 | 146 | |
|
142 | 147 | def get_kernel_ports(self, kernel_id): |
|
143 | 148 | """Return a dictionary of ports for a kernel. |
|
144 | 149 | |
|
145 | 150 | Parameters |
|
146 | 151 | ========== |
|
147 | 152 | kernel_id : uuid |
|
148 | 153 | The id of the kernel. |
|
149 | 154 | |
|
150 | 155 | Returns |
|
151 | 156 | ======= |
|
152 | 157 | port_dict : dict |
|
153 | 158 | A dict of key, value pairs where the keys are the names |
|
154 | 159 | (stdin_port,iopub_port,shell_port) and the values are the |
|
155 | 160 | integer port numbers for those channels. |
|
156 | 161 | """ |
|
157 | 162 | d = self._kernels.get(kernel_id) |
|
158 | 163 | if d is not None: |
|
159 | 164 | dcopy = d.copy() |
|
160 | 165 | dcopy.pop('process') |
|
161 | 166 | dcopy.pop('ip') |
|
162 | 167 | return dcopy |
|
163 | 168 | else: |
|
164 | 169 | raise KeyError("Kernel with id not found: %s" % kernel_id) |
|
165 | 170 | |
|
166 | 171 | def get_kernel_ip(self, kernel_id): |
|
167 | 172 | """Return ip address for a kernel. |
|
168 | 173 | |
|
169 | 174 | Parameters |
|
170 | 175 | ========== |
|
171 | 176 | kernel_id : uuid |
|
172 | 177 | The id of the kernel. |
|
173 | 178 | |
|
174 | 179 | Returns |
|
175 | 180 | ======= |
|
176 | 181 | ip : str |
|
177 | 182 | The ip address of the kernel. |
|
178 | 183 | """ |
|
179 | 184 | d = self._kernels.get(kernel_id) |
|
180 | 185 | if d is not None: |
|
181 | 186 | return d['ip'] |
|
182 | 187 | else: |
|
183 | 188 | raise KeyError("Kernel with id not found: %s" % kernel_id) |
|
184 | 189 | |
|
185 | 190 | def create_connected_stream(self, ip, port, socket_type): |
|
186 | 191 | sock = self.context.socket(socket_type) |
|
187 | 192 | addr = "tcp://%s:%i" % (ip, port) |
|
188 | 193 | self.log.info("Connecting to: %s" % addr) |
|
189 | 194 | sock.connect(addr) |
|
190 | 195 | return ZMQStream(sock) |
|
191 | 196 | |
|
192 | 197 | def create_iopub_stream(self, kernel_id): |
|
193 | 198 | ip = self.get_kernel_ip(kernel_id) |
|
194 | 199 | ports = self.get_kernel_ports(kernel_id) |
|
195 | 200 | iopub_stream = self.create_connected_stream(ip, ports['iopub_port'], zmq.SUB) |
|
196 | 201 | iopub_stream.socket.setsockopt(zmq.SUBSCRIBE, b'') |
|
197 | 202 | return iopub_stream |
|
198 | 203 | |
|
199 | 204 | def create_shell_stream(self, kernel_id): |
|
200 | 205 | ip = self.get_kernel_ip(kernel_id) |
|
201 | 206 | ports = self.get_kernel_ports(kernel_id) |
|
202 | 207 | shell_stream = self.create_connected_stream(ip, ports['shell_port'], zmq.XREQ) |
|
203 | 208 | return shell_stream |
|
204 | 209 | |
|
205 | 210 | def create_hb_stream(self, kernel_id): |
|
206 | 211 | ip = self.get_kernel_ip(kernel_id) |
|
207 | 212 | ports = self.get_kernel_ports(kernel_id) |
|
208 | 213 | hb_stream = self.create_connected_stream(ip, ports['hb_port'], zmq.REQ) |
|
209 | 214 | return hb_stream |
|
210 | 215 | |
|
211 | 216 | |
|
212 | 217 | class MappingKernelManager(KernelManager): |
|
213 | 218 | """A KernelManager that handles notebok mapping and HTTP error handling""" |
|
214 | 219 | |
|
215 | 220 | kernel_argv = List(Unicode) |
|
216 | 221 | kernel_manager = Instance(KernelManager) |
|
217 | 222 | time_to_dead = Float(3.0, config=True, help="""Kernel heartbeat interval in seconds.""") |
|
218 | 223 | max_msg_size = Int(65536, config=True, help=""" |
|
219 | 224 | The max raw message size accepted from the browser |
|
220 | 225 | over a WebSocket connection. |
|
221 | 226 | """) |
|
222 | 227 | |
|
223 | 228 | _notebook_mapping = Dict() |
|
224 | 229 | |
|
225 | 230 | #------------------------------------------------------------------------- |
|
226 | 231 | # Methods for managing kernels and sessions |
|
227 | 232 | #------------------------------------------------------------------------- |
|
228 | 233 | |
|
229 | 234 | def kernel_for_notebook(self, notebook_id): |
|
230 | 235 | """Return the kernel_id for a notebook_id or None.""" |
|
231 | 236 | return self._notebook_mapping.get(notebook_id) |
|
232 | 237 | |
|
233 | 238 | def set_kernel_for_notebook(self, notebook_id, kernel_id): |
|
234 | 239 | """Associate a notebook with a kernel.""" |
|
235 | 240 | if notebook_id is not None: |
|
236 | 241 | self._notebook_mapping[notebook_id] = kernel_id |
|
237 | 242 | |
|
238 | 243 | def notebook_for_kernel(self, kernel_id): |
|
239 | 244 | """Return the notebook_id for a kernel_id or None.""" |
|
240 | 245 | notebook_ids = [k for k, v in self._notebook_mapping.iteritems() if v == kernel_id] |
|
241 | 246 | if len(notebook_ids) == 1: |
|
242 | 247 | return notebook_ids[0] |
|
243 | 248 | else: |
|
244 | 249 | return None |
|
245 | 250 | |
|
246 | 251 | def delete_mapping_for_kernel(self, kernel_id): |
|
247 | 252 | """Remove the kernel/notebook mapping for kernel_id.""" |
|
248 | 253 | notebook_id = self.notebook_for_kernel(kernel_id) |
|
249 | 254 | if notebook_id is not None: |
|
250 | 255 | del self._notebook_mapping[notebook_id] |
|
251 | 256 | |
|
252 | 257 | def start_kernel(self, notebook_id=None): |
|
253 | 258 | """Start a kernel for a notebok an return its kernel_id. |
|
254 | 259 | |
|
255 | 260 | Parameters |
|
256 | 261 | ---------- |
|
257 | 262 | notebook_id : uuid |
|
258 | 263 | The uuid of the notebook to associate the new kernel with. If this |
|
259 | 264 | is not None, this kernel will be persistent whenever the notebook |
|
260 | 265 | requests a kernel. |
|
261 | 266 | """ |
|
262 | 267 | kernel_id = self.kernel_for_notebook(notebook_id) |
|
263 | 268 | if kernel_id is None: |
|
264 | 269 | kwargs = dict() |
|
265 | 270 | kwargs['extra_arguments'] = self.kernel_argv |
|
266 | 271 | kernel_id = super(MappingKernelManager, self).start_kernel(**kwargs) |
|
267 | 272 | self.set_kernel_for_notebook(notebook_id, kernel_id) |
|
268 | 273 | self.log.info("Kernel started: %s" % kernel_id) |
|
269 | 274 | self.log.debug("Kernel args: %r" % kwargs) |
|
270 | 275 | else: |
|
271 | 276 | self.log.info("Using existing kernel: %s" % kernel_id) |
|
272 | 277 | return kernel_id |
|
273 | 278 | |
|
274 | 279 | def kill_kernel(self, kernel_id): |
|
275 | 280 | """Kill a kernel and remove its notebook association.""" |
|
276 | 281 | if kernel_id not in self: |
|
277 | 282 | raise web.HTTPError(404) |
|
278 | 283 | super(MappingKernelManager, self).kill_kernel(kernel_id) |
|
279 | 284 | self.delete_mapping_for_kernel(kernel_id) |
|
280 | 285 | self.log.info("Kernel killed: %s" % kernel_id) |
|
281 | 286 | |
|
282 | 287 | def interrupt_kernel(self, kernel_id): |
|
283 | 288 | """Interrupt a kernel.""" |
|
284 | 289 | if kernel_id not in self: |
|
285 | 290 | raise web.HTTPError(404) |
|
286 | 291 | super(MappingKernelManager, self).interrupt_kernel(kernel_id) |
|
287 | 292 | self.log.info("Kernel interrupted: %s" % kernel_id) |
|
288 | 293 | |
|
289 | 294 | def restart_kernel(self, kernel_id): |
|
290 | 295 | """Restart a kernel while keeping clients connected.""" |
|
291 | 296 | if kernel_id not in self: |
|
292 | 297 | raise web.HTTPError(404) |
|
293 | 298 | |
|
294 | 299 | # Get the notebook_id to preserve the kernel/notebook association. |
|
295 | 300 | notebook_id = self.notebook_for_kernel(kernel_id) |
|
296 | 301 | # Create the new kernel first so we can move the clients over. |
|
297 | 302 | new_kernel_id = self.start_kernel() |
|
298 | 303 | # Now kill the old kernel. |
|
299 | 304 | self.kill_kernel(kernel_id) |
|
300 | 305 | # Now save the new kernel/notebook association. We have to save it |
|
301 | 306 | # after the old kernel is killed as that will delete the mapping. |
|
302 | 307 | self.set_kernel_for_notebook(notebook_id, new_kernel_id) |
|
303 | 308 | self.log.info("Kernel restarted: %s" % new_kernel_id) |
|
304 | 309 | return new_kernel_id |
|
305 | 310 | |
|
306 | 311 | def create_iopub_stream(self, kernel_id): |
|
307 | 312 | if kernel_id not in self: |
|
308 | 313 | raise web.HTTPError(404) |
|
309 | 314 | return super(MappingKernelManager, self).create_iopub_stream(kernel_id) |
|
310 | 315 | |
|
311 | 316 | def create_shell_stream(self, kernel_id): |
|
312 | 317 | if kernel_id not in self: |
|
313 | 318 | raise web.HTTPError(404) |
|
314 | 319 | return super(MappingKernelManager, self).create_shell_stream(kernel_id) |
|
315 | 320 | |
|
316 | 321 | def create_hb_stream(self, kernel_id): |
|
317 | 322 | if kernel_id not in self: |
|
318 | 323 | raise web.HTTPError(404) |
|
319 | 324 | return super(MappingKernelManager, self).create_hb_stream(kernel_id) |
|
320 | 325 |
@@ -1,270 +1,276 b'' | |||
|
1 |
"""A tornado based IPython notebook server. |
|
|
1 | """A tornado based IPython notebook server. | |
|
2 | ||
|
3 | Authors: | |
|
4 | ||
|
5 | * Brian Granger | |
|
6 | """ | |
|
2 | 7 | |
|
3 | 8 | #----------------------------------------------------------------------------- |
|
4 | # Copyright (C) 2011 The IPython Development Team | |
|
9 | # Copyright (C) 2008-2011 The IPython Development Team | |
|
5 | 10 | # |
|
6 | 11 | # Distributed under the terms of the BSD License. The full license is in |
|
7 |
# the file COPYING |
|
|
12 | # the file COPYING, distributed as part of this software. | |
|
8 | 13 | #----------------------------------------------------------------------------- |
|
9 | 14 | |
|
10 | 15 | #----------------------------------------------------------------------------- |
|
11 | 16 | # Imports |
|
12 | 17 | #----------------------------------------------------------------------------- |
|
13 | 18 | |
|
14 | 19 | import errno |
|
15 | 20 | import logging |
|
16 | 21 | import os |
|
17 | 22 | import signal |
|
18 | 23 | import socket |
|
19 | 24 | import sys |
|
20 | 25 | |
|
21 | 26 | import zmq |
|
22 | 27 | |
|
23 | 28 | # Install the pyzmq ioloop. This has to be done before anything else from |
|
24 | 29 | # tornado is imported. |
|
25 | 30 | from zmq.eventloop import ioloop |
|
26 | 31 | import tornado.ioloop |
|
27 | 32 | tornado.ioloop = ioloop |
|
28 | 33 | |
|
29 | 34 | from tornado import httpserver |
|
30 | 35 | from tornado import web |
|
31 | 36 | |
|
32 | 37 | from .kernelmanager import MappingKernelManager |
|
33 | 38 | from .handlers import ( |
|
34 | 39 | NBBrowserHandler, NewHandler, NamedNotebookHandler, |
|
35 | 40 | MainKernelHandler, KernelHandler, KernelActionHandler, IOPubHandler, |
|
36 | 41 | ShellHandler, NotebookRootHandler, NotebookHandler, RSTHandler |
|
37 | 42 | ) |
|
38 | 43 | from .notebookmanager import NotebookManager |
|
39 | 44 | |
|
40 | 45 | from IPython.core.application import BaseIPythonApplication |
|
41 | 46 | from IPython.core.profiledir import ProfileDir |
|
42 | 47 | from IPython.zmq.session import Session |
|
43 | 48 | from IPython.zmq.zmqshell import ZMQInteractiveShell |
|
44 | 49 | from IPython.zmq.ipkernel import ( |
|
45 | 50 | flags as ipkernel_flags, |
|
46 | 51 | aliases as ipkernel_aliases, |
|
47 | 52 | IPKernelApp |
|
48 | 53 | ) |
|
49 | 54 | from IPython.utils.traitlets import Dict, Unicode, Int, List, Enum |
|
50 | 55 | |
|
51 | 56 | #----------------------------------------------------------------------------- |
|
52 | 57 | # Module globals |
|
53 | 58 | #----------------------------------------------------------------------------- |
|
54 | 59 | |
|
55 | 60 | _kernel_id_regex = r"(?P<kernel_id>\w+-\w+-\w+-\w+-\w+)" |
|
56 | 61 | _kernel_action_regex = r"(?P<action>restart|interrupt)" |
|
57 | 62 | _notebook_id_regex = r"(?P<notebook_id>\w+-\w+-\w+-\w+-\w+)" |
|
58 | 63 | |
|
59 | 64 | LOCALHOST = '127.0.0.1' |
|
60 | 65 | |
|
61 | 66 | _examples = """ |
|
62 | 67 | ipython notebook # start the notebook |
|
63 | 68 | ipython notebook --profile=sympy # use the sympy profile |
|
64 | 69 | ipython notebook --pylab=inline # pylab in inline plotting mode |
|
65 | 70 | ipython notebook --certfile=mycert.pem # use SSL/TLS certificate |
|
66 | 71 | ipython notebook --port=5555 --ip=* # Listen on port 5555, all interfaces |
|
67 | 72 | """ |
|
68 | 73 | |
|
69 | 74 | #----------------------------------------------------------------------------- |
|
70 | 75 | # The Tornado web application |
|
71 | 76 | #----------------------------------------------------------------------------- |
|
72 | 77 | |
|
73 | 78 | class NotebookWebApplication(web.Application): |
|
74 | 79 | |
|
75 | 80 | def __init__(self, ipython_app, kernel_manager, notebook_manager, log): |
|
76 | 81 | handlers = [ |
|
77 | 82 | (r"/", NBBrowserHandler), |
|
78 | 83 | (r"/new", NewHandler), |
|
79 | 84 | (r"/%s" % _notebook_id_regex, NamedNotebookHandler), |
|
80 | 85 | (r"/kernels", MainKernelHandler), |
|
81 | 86 | (r"/kernels/%s" % _kernel_id_regex, KernelHandler), |
|
82 | 87 | (r"/kernels/%s/%s" % (_kernel_id_regex, _kernel_action_regex), KernelActionHandler), |
|
83 | 88 | (r"/kernels/%s/iopub" % _kernel_id_regex, IOPubHandler), |
|
84 | 89 | (r"/kernels/%s/shell" % _kernel_id_regex, ShellHandler), |
|
85 | 90 | (r"/notebooks", NotebookRootHandler), |
|
86 | 91 | (r"/notebooks/%s" % _notebook_id_regex, NotebookHandler), |
|
87 | 92 | (r"/rstservice/render", RSTHandler) |
|
88 | 93 | ] |
|
89 | 94 | settings = dict( |
|
90 | 95 | template_path=os.path.join(os.path.dirname(__file__), "templates"), |
|
91 | 96 | static_path=os.path.join(os.path.dirname(__file__), "static"), |
|
92 | 97 | ) |
|
93 | 98 | web.Application.__init__(self, handlers, **settings) |
|
94 | 99 | |
|
95 | 100 | self.kernel_manager = kernel_manager |
|
96 | 101 | self.log = log |
|
97 | 102 | self.notebook_manager = notebook_manager |
|
98 | 103 | self.ipython_app = ipython_app |
|
99 | 104 | |
|
100 | 105 | |
|
101 | 106 | #----------------------------------------------------------------------------- |
|
102 | 107 | # Aliases and Flags |
|
103 | 108 | #----------------------------------------------------------------------------- |
|
104 | 109 | |
|
105 | 110 | flags = dict(ipkernel_flags) |
|
106 | 111 | |
|
107 | 112 | # the flags that are specific to the frontend |
|
108 | 113 | # these must be scrubbed before being passed to the kernel, |
|
109 | 114 | # or it will raise an error on unrecognized flags |
|
110 | 115 | notebook_flags = [] |
|
111 | 116 | |
|
112 | 117 | aliases = dict(ipkernel_aliases) |
|
113 | 118 | |
|
114 | 119 | aliases.update({ |
|
115 | 120 | 'ip': 'IPythonNotebookApp.ip', |
|
116 | 121 | 'port': 'IPythonNotebookApp.port', |
|
117 | 122 | 'keyfile': 'IPythonNotebookApp.keyfile', |
|
118 | 123 | 'certfile': 'IPythonNotebookApp.certfile', |
|
119 | 124 | 'ws-hostname': 'IPythonNotebookApp.ws_hostname', |
|
120 | 125 | 'notebook-dir': 'NotebookManager.notebook_dir' |
|
121 | 126 | }) |
|
122 | 127 | |
|
123 | 128 | notebook_aliases = [u'port', u'ip', u'keyfile', u'certfile', u'ws-hostname', |
|
124 | 129 | u'notebook-dir'] |
|
125 | 130 | |
|
126 | 131 | #----------------------------------------------------------------------------- |
|
127 | 132 | # IPythonNotebookApp |
|
128 | 133 | #----------------------------------------------------------------------------- |
|
129 | 134 | |
|
130 | 135 | class IPythonNotebookApp(BaseIPythonApplication): |
|
131 | 136 | |
|
132 | 137 | name = 'ipython-notebook' |
|
133 | 138 | default_config_file_name='ipython_notebook_config.py' |
|
134 | 139 | |
|
135 | 140 | description = """ |
|
136 | 141 | The IPython HTML Notebook. |
|
137 | 142 | |
|
138 | 143 | This launches a Tornado based HTML Notebook Server that serves up an |
|
139 | 144 | HTML5/Javascript Notebook client. |
|
140 | 145 | """ |
|
141 | 146 | examples = _examples |
|
142 | 147 | |
|
143 | 148 | classes = [IPKernelApp, ZMQInteractiveShell, ProfileDir, Session, |
|
144 | 149 | MappingKernelManager, NotebookManager] |
|
145 | 150 | flags = Dict(flags) |
|
146 | 151 | aliases = Dict(aliases) |
|
147 | 152 | |
|
148 | 153 | kernel_argv = List(Unicode) |
|
149 | 154 | |
|
150 | 155 | log_level = Enum((0,10,20,30,40,50,'DEBUG','INFO','WARN','ERROR','CRITICAL'), |
|
151 | 156 | default_value=logging.INFO, |
|
152 | 157 | config=True, |
|
153 | 158 | help="Set the log level by value or name.") |
|
154 | 159 | |
|
155 | 160 | # Network related information. |
|
156 | 161 | |
|
157 | 162 | ip = Unicode(LOCALHOST, config=True, |
|
158 | 163 | help="The IP address the notebook server will listen on." |
|
159 | 164 | ) |
|
160 | 165 | |
|
161 | 166 | def _ip_changed(self, name, old, new): |
|
162 | 167 | if new == u'*': self.ip = u'' |
|
163 | 168 | |
|
164 | 169 | port = Int(8888, config=True, |
|
165 | 170 | help="The port the notebook server will listen on." |
|
166 | 171 | ) |
|
167 | 172 | |
|
168 | 173 | ws_hostname = Unicode(LOCALHOST, config=True, |
|
169 | 174 | help="""The FQDN or IP for WebSocket connections. The default will work |
|
170 | 175 | fine when the server is listening on localhost, but this needs to |
|
171 | 176 | be set if the ip option is used. It will be used as the hostname part |
|
172 | 177 | of the WebSocket url: ws://hostname/path.""" |
|
173 | 178 | ) |
|
174 | 179 | |
|
175 | 180 | certfile = Unicode(u'', config=True, |
|
176 | 181 | help="""The full path to an SSL/TLS certificate file.""" |
|
177 | 182 | ) |
|
178 | 183 | |
|
179 | 184 | keyfile = Unicode(u'', config=True, |
|
180 | 185 | help="""The full path to a private key file for usage with SSL/TLS.""" |
|
181 | 186 | ) |
|
182 | 187 | |
|
183 | 188 | def get_ws_url(self): |
|
184 | 189 | """Return the WebSocket URL for this server.""" |
|
185 | 190 | if self.certfile: |
|
186 | 191 | prefix = u'wss://' |
|
187 | 192 | else: |
|
188 | 193 | prefix = u'ws://' |
|
189 | 194 | return prefix + self.ws_hostname + u':' + unicode(self.port) |
|
190 | 195 | |
|
191 | 196 | def parse_command_line(self, argv=None): |
|
192 | 197 | super(IPythonNotebookApp, self).parse_command_line(argv) |
|
193 | 198 | if argv is None: |
|
194 | 199 | argv = sys.argv[1:] |
|
195 | 200 | |
|
196 | 201 | self.kernel_argv = list(argv) # copy |
|
197 |
# |
|
|
202 | # Kernel should inherit default config file from frontend | |
|
198 | 203 | self.kernel_argv.append("--KernelApp.parent_appname='%s'"%self.name) |
|
199 |
# |
|
|
204 | # Scrub frontend-specific flags | |
|
200 | 205 | for a in argv: |
|
201 | 206 | if a.startswith('-') and a.lstrip('-') in notebook_flags: |
|
202 | 207 | self.kernel_argv.remove(a) |
|
203 | 208 | for a in argv: |
|
204 | 209 | if a.startswith('-'): |
|
205 | 210 | alias = a.lstrip('-').split('=')[0] |
|
206 | 211 | if alias in notebook_aliases: |
|
207 | 212 | self.kernel_argv.remove(a) |
|
208 | print self.kernel_argv | |
|
209 | 213 | |
|
210 | 214 | def init_configurables(self): |
|
211 | 215 | # Don't let Qt or ZMQ swallow KeyboardInterupts. |
|
212 | 216 | signal.signal(signal.SIGINT, signal.SIG_DFL) |
|
213 | 217 | |
|
214 | 218 | # Create a KernelManager and start a kernel. |
|
215 | 219 | self.kernel_manager = MappingKernelManager( |
|
216 | 220 | config=self.config, log=self.log, kernel_argv=self.kernel_argv |
|
217 | 221 | ) |
|
218 | 222 | self.notebook_manager = NotebookManager(config=self.config, log=self.log) |
|
219 | 223 | |
|
220 | 224 | def init_logging(self): |
|
221 | 225 | super(IPythonNotebookApp, self).init_logging() |
|
222 | 226 | # This prevents double log messages because tornado use a root logger that |
|
223 | 227 | # self.log is a child of. The logging module dipatches log messages to a log |
|
224 | 228 | # and all of its ancenstors until propagate is set to False. |
|
225 | 229 | self.log.propagate = False |
|
226 | 230 | |
|
227 | 231 | def initialize(self, argv=None): |
|
228 | 232 | super(IPythonNotebookApp, self).initialize(argv) |
|
229 | 233 | self.init_configurables() |
|
230 | 234 | self.web_app = NotebookWebApplication( |
|
231 | 235 | self, self.kernel_manager, self.notebook_manager, self.log |
|
232 | 236 | ) |
|
233 | 237 | if self.certfile: |
|
234 | 238 | ssl_options = dict(certfile=self.certfile) |
|
235 | 239 | if self.keyfile: |
|
236 | 240 | ssl_options['keyfile'] = self.keyfile |
|
237 | 241 | else: |
|
238 | 242 | ssl_options = None |
|
239 | 243 | self.http_server = httpserver.HTTPServer(self.web_app, ssl_options=ssl_options) |
|
240 | 244 | if ssl_options is None and not self.ip: |
|
241 | 245 | self.log.critical('WARNING: the notebook server is listening on all IP addresses ' |
|
242 | 246 | 'but not using any encryption or authentication. This is highly ' |
|
243 | 247 | 'insecure and not recommended.') |
|
244 | for i in range(10): | |
|
248 | ||
|
249 | # Try random ports centered around the default. | |
|
250 | from random import randint | |
|
251 | n = 50 # Max number of attempts, keep reasonably large. | |
|
252 | for port in [self.port] + [self.port + randint(-2*n, 2*n) for i in range(n)]: | |
|
245 | 253 | try: |
|
246 | port = self.port + i | |
|
247 | 254 | self.http_server.listen(port, self.ip) |
|
248 | 255 | except socket.error, e: |
|
249 | 256 | if e.errno != errno.EADDRINUSE: |
|
250 | 257 | raise |
|
251 |
self.log.info('The port %i is already in use, trying |
|
|
258 | self.log.info('The port %i is already in use, trying another random port.' % port) | |
|
252 | 259 | else: |
|
253 | 260 | self.port = port |
|
254 | 261 | break |
|
255 | ||
|
256 | 262 | |
|
257 | 263 | def start(self): |
|
258 | 264 | ip = self.ip if self.ip else '[all ip addresses on your system]' |
|
259 | 265 | self.log.info("The IPython Notebook is running at: http://%s:%i" % (ip, self.port)) |
|
260 | 266 | ioloop.IOLoop.instance().start() |
|
261 | 267 | |
|
262 | 268 | #----------------------------------------------------------------------------- |
|
263 | 269 | # Main entry point |
|
264 | 270 | #----------------------------------------------------------------------------- |
|
265 | 271 | |
|
266 | 272 | def launch_new_instance(): |
|
267 | 273 | app = IPythonNotebookApp() |
|
268 | 274 | app.initialize() |
|
269 | 275 | app.start() |
|
270 | 276 |
@@ -1,227 +1,232 b'' | |||
|
1 | """A notebook manager that uses the local file system for storage. | |
|
2 | ||
|
3 | Authors: | |
|
4 | ||
|
5 | * Brian Granger | |
|
6 | """ | |
|
7 | ||
|
1 | 8 | #----------------------------------------------------------------------------- |
|
2 | # Copyright (C) 2011 The IPython Development Team | |
|
9 | # Copyright (C) 2008-2011 The IPython Development Team | |
|
3 | 10 | # |
|
4 | 11 | # Distributed under the terms of the BSD License. The full license is in |
|
5 |
# the file COPYING |
|
|
12 | # the file COPYING, distributed as part of this software. | |
|
6 | 13 | #----------------------------------------------------------------------------- |
|
7 | 14 | |
|
8 | 15 | #----------------------------------------------------------------------------- |
|
9 | 16 | # Imports |
|
10 | 17 | #----------------------------------------------------------------------------- |
|
11 | 18 | |
|
12 | 19 | import datetime |
|
13 | 20 | import os |
|
14 | 21 | import uuid |
|
15 | 22 | |
|
16 | 23 | from tornado import web |
|
17 | 24 | |
|
18 | 25 | from IPython.config.configurable import LoggingConfigurable |
|
19 | 26 | from IPython.nbformat import current |
|
20 | 27 | from IPython.utils.traitlets import Unicode, List, Dict |
|
21 | 28 | |
|
22 | 29 | |
|
23 | 30 | #----------------------------------------------------------------------------- |
|
24 | 31 | # Code |
|
25 | 32 | #----------------------------------------------------------------------------- |
|
26 | 33 | |
|
27 | 34 | |
|
28 | 35 | class NotebookManager(LoggingConfigurable): |
|
29 | 36 | |
|
30 | 37 | notebook_dir = Unicode(os.getcwd(), config=True, help=""" |
|
31 | 38 | The directory to use for notebooks. |
|
32 | 39 | """) |
|
33 | 40 | filename_ext = Unicode(u'.ipynb') |
|
34 | 41 | allowed_formats = List([u'json',u'xml',u'py']) |
|
35 | 42 | |
|
36 | 43 | # Map notebook_ids to notebook names |
|
37 | 44 | mapping = Dict() |
|
38 | 45 | # Map notebook names to notebook_ids |
|
39 | 46 | rev_mapping = Dict() |
|
40 | 47 | |
|
41 | 48 | def list_notebooks(self): |
|
42 | 49 | """List all notebooks in the notebook dir. |
|
43 | 50 | |
|
44 | 51 | This returns a list of dicts of the form:: |
|
45 | 52 | |
|
46 | 53 | dict(notebook_id=notebook,name=name) |
|
47 | 54 | """ |
|
48 | 55 | names = os.listdir(self.notebook_dir) |
|
49 |
names = [name.split(u'.')[0] |
|
|
56 | names = [name.split(u'.')[0] | |
|
50 | 57 | for name in names if name.endswith(self.filename_ext)] |
|
51 | 58 | data = [] |
|
52 | 59 | for name in names: |
|
53 | 60 | if name not in self.rev_mapping: |
|
54 | 61 | notebook_id = self.new_notebook_id(name) |
|
55 | 62 | else: |
|
56 | 63 | notebook_id = self.rev_mapping[name] |
|
57 | 64 | data.append(dict(notebook_id=notebook_id,name=name)) |
|
58 | 65 | data = sorted(data, key=lambda item: item['name']) |
|
59 | 66 | return data |
|
60 | 67 | |
|
61 | 68 | def new_notebook_id(self, name): |
|
62 | 69 | """Generate a new notebook_id for a name and store its mappings.""" |
|
63 | 70 | notebook_id = unicode(uuid.uuid4()) |
|
64 | 71 | self.mapping[notebook_id] = name |
|
65 | 72 | self.rev_mapping[name] = notebook_id |
|
66 | 73 | return notebook_id |
|
67 | 74 | |
|
68 | 75 | def delete_notebook_id(self, notebook_id): |
|
69 | 76 | """Delete a notebook's id only. This doesn't delete the actual notebook.""" |
|
70 | 77 | name = self.mapping[notebook_id] |
|
71 | 78 | del self.mapping[notebook_id] |
|
72 | 79 | del self.rev_mapping[name] |
|
73 | 80 | |
|
74 | 81 | def notebook_exists(self, notebook_id): |
|
75 | 82 | """Does a notebook exist?""" |
|
76 | 83 | if notebook_id not in self.mapping: |
|
77 | 84 | return False |
|
78 | 85 | path = self.get_path_by_name(self.mapping[notebook_id]) |
|
79 |
|
|
|
80 | return False | |
|
81 | return True | |
|
86 | return os.path.isfile(path) | |
|
82 | 87 | |
|
83 | 88 | def find_path(self, notebook_id): |
|
84 | 89 | """Return a full path to a notebook given its notebook_id.""" |
|
85 | 90 | try: |
|
86 | 91 | name = self.mapping[notebook_id] |
|
87 | 92 | except KeyError: |
|
88 | 93 | raise web.HTTPError(404) |
|
89 | 94 | return self.get_path_by_name(name) |
|
90 | 95 | |
|
91 | 96 | def get_path_by_name(self, name): |
|
92 | 97 | """Return a full path to a notebook given its name.""" |
|
93 | 98 | filename = name + self.filename_ext |
|
94 | 99 | path = os.path.join(self.notebook_dir, filename) |
|
95 | 100 | return path |
|
96 | 101 | |
|
97 | 102 | def get_notebook(self, notebook_id, format=u'json'): |
|
98 | 103 | """Get the representation of a notebook in format by notebook_id.""" |
|
99 | 104 | format = unicode(format) |
|
100 | 105 | if format not in self.allowed_formats: |
|
101 | 106 | raise web.HTTPError(415) |
|
102 | 107 | last_modified, nb = self.get_notebook_object(notebook_id) |
|
103 | 108 | data = current.writes(nb, format) |
|
104 | 109 | name = nb.get('name','notebook') |
|
105 | 110 | return last_modified, name, data |
|
106 | 111 | |
|
107 | 112 | def get_notebook_object(self, notebook_id): |
|
108 | 113 | """Get the NotebookNode representation of a notebook by notebook_id.""" |
|
109 | 114 | path = self.find_path(notebook_id) |
|
110 | 115 | if not os.path.isfile(path): |
|
111 | 116 | raise web.HTTPError(404) |
|
112 | 117 | info = os.stat(path) |
|
113 | 118 | last_modified = datetime.datetime.utcfromtimestamp(info.st_mtime) |
|
114 | 119 | try: |
|
115 | 120 | with open(path,'r') as f: |
|
116 | 121 | s = f.read() |
|
117 | 122 | try: |
|
118 | 123 | # v2 and later have xml in the .ipynb files. |
|
119 | 124 | nb = current.reads(s, 'xml') |
|
120 | 125 | except: |
|
121 | 126 | # v1 had json in the .ipynb files. |
|
122 | 127 | nb = current.reads(s, 'json') |
|
123 | 128 | # v1 notebooks don't have a name field, so use the filename. |
|
124 | 129 | nb.name = os.path.split(path)[-1].split(u'.')[0] |
|
125 | 130 | except: |
|
126 | 131 | raise web.HTTPError(404) |
|
127 | 132 | return last_modified, nb |
|
128 | 133 | |
|
129 | 134 | def save_new_notebook(self, data, name=None, format=u'json'): |
|
130 | 135 | """Save a new notebook and return its notebook_id. |
|
131 | 136 | |
|
132 | 137 | If a name is passed in, it overrides any values in the notebook data |
|
133 | 138 | and the value in the data is updated to use that value. |
|
134 | 139 | """ |
|
135 | 140 | if format not in self.allowed_formats: |
|
136 | 141 | raise web.HTTPError(415) |
|
137 | 142 | |
|
138 | 143 | try: |
|
139 | 144 | nb = current.reads(data, format) |
|
140 | 145 | except: |
|
141 | 146 | if format == u'xml': |
|
142 | 147 | # v1 notebooks might come in with a format='xml' but be json. |
|
143 | 148 | try: |
|
144 | 149 | nb = current.reads(data, u'json') |
|
145 | 150 | except: |
|
146 | 151 | raise web.HTTPError(400) |
|
147 | 152 | else: |
|
148 | 153 | raise web.HTTPError(400) |
|
149 | 154 | |
|
150 | 155 | if name is None: |
|
151 | 156 | try: |
|
152 | 157 | name = nb.name |
|
153 | 158 | except AttributeError: |
|
154 | 159 | raise web.HTTPError(400) |
|
155 | 160 | nb.name = name |
|
156 | 161 | |
|
157 | 162 | notebook_id = self.new_notebook_id(name) |
|
158 | 163 | self.save_notebook_object(notebook_id, nb) |
|
159 | 164 | return notebook_id |
|
160 | 165 | |
|
161 | 166 | def save_notebook(self, notebook_id, data, name=None, format=u'json'): |
|
162 | 167 | """Save an existing notebook by notebook_id.""" |
|
163 | 168 | if format not in self.allowed_formats: |
|
164 | 169 | raise web.HTTPError(415) |
|
165 | 170 | |
|
166 | 171 | try: |
|
167 | 172 | nb = current.reads(data, format) |
|
168 | 173 | except: |
|
169 | 174 | if format == u'xml': |
|
170 | 175 | # v1 notebooks might come in with a format='xml' but be json. |
|
171 | 176 | try: |
|
172 | 177 | nb = current.reads(data, u'json') |
|
173 | 178 | except: |
|
174 | 179 | raise web.HTTPError(400) |
|
175 | 180 | else: |
|
176 | 181 | raise web.HTTPError(400) |
|
177 | 182 | |
|
178 | 183 | if name is not None: |
|
179 | 184 | nb.name = name |
|
180 | 185 | self.save_notebook_object(notebook_id, nb) |
|
181 | 186 | |
|
182 | 187 | def save_notebook_object(self, notebook_id, nb): |
|
183 | 188 | """Save an existing notebook object by notebook_id.""" |
|
184 | 189 | if notebook_id not in self.mapping: |
|
185 | 190 | raise web.HTTPError(404) |
|
186 | 191 | old_name = self.mapping[notebook_id] |
|
187 | 192 | try: |
|
188 | 193 | new_name = nb.name |
|
189 | 194 | except AttributeError: |
|
190 | 195 | raise web.HTTPError(400) |
|
191 | 196 | path = self.get_path_by_name(new_name) |
|
192 | 197 | try: |
|
193 | 198 | with open(path,'w') as f: |
|
194 | 199 | current.write(nb, f, u'xml') |
|
195 | 200 | except: |
|
196 | 201 | raise web.HTTPError(400) |
|
197 | 202 | if old_name != new_name: |
|
198 | 203 | old_path = self.get_path_by_name(old_name) |
|
199 | 204 | if os.path.isfile(old_path): |
|
200 | 205 | os.unlink(old_path) |
|
201 | 206 | self.mapping[notebook_id] = new_name |
|
202 | 207 | self.rev_mapping[new_name] = notebook_id |
|
203 | 208 | |
|
204 | 209 | def delete_notebook(self, notebook_id): |
|
205 | 210 | """Delete notebook by notebook_id.""" |
|
206 | 211 | path = self.find_path(notebook_id) |
|
207 | 212 | if not os.path.isfile(path): |
|
208 | 213 | raise web.HTTPError(404) |
|
209 | 214 | os.unlink(path) |
|
210 | 215 | self.delete_notebook_id(notebook_id) |
|
211 | 216 | |
|
212 | 217 | def new_notebook(self): |
|
213 | 218 | """Create a new notebook and returns its notebook_id.""" |
|
214 | 219 | i = 0 |
|
215 | 220 | while True: |
|
216 | 221 | name = u'Untitled%i' % i |
|
217 | 222 | path = self.get_path_by_name(name) |
|
218 | 223 | if not os.path.isfile(path): |
|
219 | 224 | break |
|
220 | 225 | else: |
|
221 | 226 | i = i+1 |
|
222 | 227 | notebook_id = self.new_notebook_id(name) |
|
223 | 228 | nb = current.new_notebook(name=name) |
|
224 | 229 | with open(path,'w') as f: |
|
225 | 230 | current.write(nb, f, u'xml') |
|
226 | 231 | return notebook_id |
|
227 | 232 |
@@ -1,87 +1,93 b'' | |||
|
1 | //---------------------------------------------------------------------------- | |
|
2 | // Copyright (C) 2008-2011 The IPython Development Team | |
|
3 | // | |
|
4 | // Distributed under the terms of the BSD License. The full license is in | |
|
5 | // the file COPYING, distributed as part of this software. | |
|
6 | //---------------------------------------------------------------------------- | |
|
1 | 7 |
|
|
2 | 8 | //============================================================================ |
|
3 | 9 | // Cell |
|
4 | 10 | //============================================================================ |
|
5 | 11 | |
|
6 | 12 | var IPython = (function (IPython) { |
|
7 | 13 | |
|
8 | 14 | var utils = IPython.utils; |
|
9 | 15 | |
|
10 | 16 | var Cell = function (notebook) { |
|
11 | 17 | this.notebook = notebook; |
|
12 | 18 | this.selected = false; |
|
13 | 19 | this.element = null; |
|
14 | 20 | this.create_element(); |
|
15 | 21 | if (this.element !== null) { |
|
16 | 22 | this.set_autoindent(true); |
|
17 | 23 | this.element.data("cell", this); |
|
18 | 24 | this.bind_events(); |
|
19 | 25 | } |
|
20 | 26 | this.cell_id = utils.uuid(); |
|
21 | 27 | }; |
|
22 | 28 | |
|
23 | 29 | |
|
24 | 30 | Cell.prototype.select = function () { |
|
25 | 31 | this.element.addClass('ui-widget-content ui-corner-all'); |
|
26 | 32 | this.selected = true; |
|
27 | 33 | }; |
|
28 | 34 | |
|
29 | 35 | |
|
30 | 36 | Cell.prototype.unselect = function () { |
|
31 | 37 | this.element.removeClass('ui-widget-content ui-corner-all'); |
|
32 | 38 | this.selected = false; |
|
33 | 39 | }; |
|
34 | 40 | |
|
35 | 41 | |
|
36 | 42 | Cell.prototype.bind_events = function () { |
|
37 | 43 | var that = this; |
|
38 | 44 | var nb = that.notebook |
|
39 | 45 | that.element.click(function (event) { |
|
40 | 46 | if (that.selected === false) { |
|
41 | 47 | nb.select(nb.find_cell_index(that)); |
|
42 | 48 | }; |
|
43 | 49 | }); |
|
44 | 50 | that.element.focusin(function (event) { |
|
45 | 51 | if (that.selected === false) { |
|
46 | 52 | nb.select(nb.find_cell_index(that)); |
|
47 | 53 | }; |
|
48 | 54 | }); |
|
49 | 55 | }; |
|
50 | 56 | |
|
51 | 57 | Cell.prototype.grow = function(element) { |
|
52 | 58 | // Grow the cell by hand. This is used upon reloading from JSON, when the |
|
53 | 59 | // autogrow handler is not called. |
|
54 | 60 | var dom = element.get(0); |
|
55 | 61 | var lines_count = 0; |
|
56 | 62 | // modified split rule from |
|
57 | 63 | // http://stackoverflow.com/questions/2035910/how-to-get-the-number-of-lines-in-a-textarea/2036424#2036424 |
|
58 | 64 | var lines = dom.value.split(/\r|\r\n|\n/); |
|
59 | 65 | lines_count = lines.length; |
|
60 | 66 | if (lines_count >= 1) { |
|
61 | 67 | dom.rows = lines_count; |
|
62 | 68 | } else { |
|
63 | 69 | dom.rows = 1; |
|
64 | 70 | } |
|
65 | 71 | }; |
|
66 | 72 | |
|
67 | 73 | |
|
68 | 74 | Cell.prototype.set_autoindent = function (state) { |
|
69 | 75 | if (state) { |
|
70 | 76 | this.code_mirror.setOption('tabMode', 'indent'); |
|
71 | 77 | this.code_mirror.setOption('enterMode', 'indent'); |
|
72 | 78 | } else { |
|
73 | 79 | this.code_mirror.setOption('tabMode', 'shift'); |
|
74 | 80 | this.code_mirror.setOption('enterMode', 'flat'); |
|
75 | 81 | } |
|
76 | 82 | }; |
|
77 | 83 | |
|
78 | 84 | |
|
79 | 85 | // Subclasses must implement create_element. |
|
80 | 86 | Cell.prototype.create_element = function () {}; |
|
81 | 87 | |
|
82 | 88 | IPython.Cell = Cell; |
|
83 | 89 | |
|
84 | 90 | return IPython; |
|
85 | 91 | |
|
86 | 92 | }(IPython)); |
|
87 | 93 |
@@ -1,434 +1,440 b'' | |||
|
1 | //---------------------------------------------------------------------------- | |
|
2 | // Copyright (C) 2008-2011 The IPython Development Team | |
|
3 | // | |
|
4 | // Distributed under the terms of the BSD License. The full license is in | |
|
5 | // the file COPYING, distributed as part of this software. | |
|
6 | //---------------------------------------------------------------------------- | |
|
1 | 7 |
|
|
2 | 8 | //============================================================================ |
|
3 | 9 | // CodeCell |
|
4 | 10 | //============================================================================ |
|
5 | 11 | |
|
6 | 12 | var IPython = (function (IPython) { |
|
7 | 13 | |
|
8 | 14 | var utils = IPython.utils; |
|
9 | 15 | |
|
10 | 16 | var CodeCell = function (notebook) { |
|
11 | 17 | this.code_mirror = null; |
|
12 | 18 | this.input_prompt_number = ' '; |
|
13 | 19 | this.is_completing = false; |
|
14 | 20 | this.completion_cursor = null; |
|
15 | 21 | this.outputs = []; |
|
16 | 22 | this.collapsed = false; |
|
17 | 23 | IPython.Cell.apply(this, arguments); |
|
18 | 24 | }; |
|
19 | 25 | |
|
20 | 26 | |
|
21 | 27 | CodeCell.prototype = new IPython.Cell(); |
|
22 | 28 | |
|
23 | 29 | |
|
24 | 30 | CodeCell.prototype.create_element = function () { |
|
25 | 31 | var cell = $('<div></div>').addClass('cell border-box-sizing code_cell vbox'); |
|
26 | 32 | var input = $('<div></div>').addClass('input hbox'); |
|
27 | 33 | input.append($('<div/>').addClass('prompt input_prompt')); |
|
28 | 34 | var input_area = $('<div/>').addClass('input_area box-flex1'); |
|
29 | 35 | this.code_mirror = CodeMirror(input_area.get(0), { |
|
30 | 36 | indentUnit : 4, |
|
31 | 37 | mode: 'python', |
|
32 | 38 | theme: 'ipython', |
|
33 | 39 | onKeyEvent: $.proxy(this.handle_codemirror_keyevent,this) |
|
34 | 40 | }); |
|
35 | 41 | input.append(input_area); |
|
36 | 42 | var output = $('<div></div>').addClass('output vbox'); |
|
37 | 43 | cell.append(input).append(output); |
|
38 | 44 | this.element = cell; |
|
39 | 45 | this.collapse() |
|
40 | 46 | }; |
|
41 | 47 | |
|
42 | 48 | |
|
43 | 49 | CodeCell.prototype.handle_codemirror_keyevent = function (editor, event) { |
|
44 | 50 | // This method gets called in CodeMirror's onKeyDown/onKeyPress handlers and |
|
45 | 51 | // is used to provide custom key handling. Its return value is used to determine |
|
46 | 52 | // if CodeMirror should ignore the event: true = ignore, false = don't ignore. |
|
47 | 53 | if (event.keyCode === 13 && event.shiftKey) { |
|
48 | 54 | // Always ignore shift-enter in CodeMirror as we handle it. |
|
49 | 55 | return true; |
|
50 | 56 | } else if (event.keyCode === 9 && event.type == 'keydown') { |
|
51 | 57 | // Tab completion. |
|
52 | 58 | var cur = editor.getCursor(); |
|
53 | 59 | var pre_cursor = editor.getRange({line:cur.line,ch:0},cur).trim(); |
|
54 | 60 | if (pre_cursor === "") { |
|
55 | 61 | // Don't autocomplete if the part of the line before the cursor is empty. |
|
56 | 62 | // In this case, let CodeMirror handle indentation. |
|
57 | 63 | return false; |
|
58 | 64 | } else { |
|
59 | 65 | // Autocomplete the current line. |
|
60 | 66 | event.stop(); |
|
61 | 67 | var line = editor.getLine(cur.line); |
|
62 | 68 | this.is_completing = true; |
|
63 | 69 | this.completion_cursor = cur; |
|
64 | 70 | IPython.notebook.complete_cell(this, line, cur.ch); |
|
65 | 71 | return true; |
|
66 | 72 | } |
|
67 | 73 | } else if (event.keyCode === 8 && event.type == 'keydown') { |
|
68 | 74 | // If backspace and the line ends with 4 spaces, remove them. |
|
69 | 75 | var cur = editor.getCursor(); |
|
70 | 76 | var line = editor.getLine(cur.line); |
|
71 | 77 | var ending = line.slice(-4); |
|
72 | 78 | if (ending === ' ') { |
|
73 | 79 | editor.replaceRange('', |
|
74 | 80 | {line: cur.line, ch: cur.ch-4}, |
|
75 | 81 | {line: cur.line, ch: cur.ch} |
|
76 | 82 | ); |
|
77 | 83 | event.stop(); |
|
78 | 84 | return true; |
|
79 | 85 | } else { |
|
80 | 86 | return false; |
|
81 | 87 | }; |
|
82 | 88 | } else { |
|
83 | 89 | // keypress/keyup also trigger on TAB press, and we don't want to use those |
|
84 | 90 | // to disable tab completion. |
|
85 | 91 | if (this.is_completing && event.keyCode !== 9) { |
|
86 | 92 | var ed_cur = editor.getCursor(); |
|
87 | 93 | var cc_cur = this.completion_cursor; |
|
88 | 94 | if (ed_cur.line !== cc_cur.line || ed_cur.ch !== cc_cur.ch) { |
|
89 | 95 | this.is_completing = false; |
|
90 | 96 | this.completion_cursor = null; |
|
91 | 97 | }; |
|
92 | 98 | }; |
|
93 | 99 | return false; |
|
94 | 100 | }; |
|
95 | 101 | }; |
|
96 | 102 | |
|
97 | 103 | |
|
98 | 104 | CodeCell.prototype.finish_completing = function (matched_text, matches) { |
|
99 | 105 | // console.log("Got matches", matched_text, matches); |
|
100 | 106 | if (!this.is_completing || matches.length === 0) {return;} |
|
101 | 107 | |
|
102 | 108 | var that = this; |
|
103 | 109 | var cur = this.completion_cursor; |
|
104 | 110 | |
|
105 | 111 | var insert = function (selected_text) { |
|
106 | 112 | that.code_mirror.replaceRange( |
|
107 | 113 | selected_text, |
|
108 | 114 | {line: cur.line, ch: (cur.ch-matched_text.length)}, |
|
109 | 115 | {line: cur.line, ch: cur.ch} |
|
110 | 116 | ); |
|
111 | 117 | }; |
|
112 | 118 | |
|
113 | 119 | if (matches.length === 1) { |
|
114 | 120 | insert(matches[0]); |
|
115 | 121 | setTimeout(function(){that.code_mirror.focus();}, 50); |
|
116 | 122 | return; |
|
117 | 123 | }; |
|
118 | 124 | |
|
119 | 125 | var complete = $('<div/>').addClass('completions'); |
|
120 | 126 | var select = $('<select/>').attr('multiple','true'); |
|
121 | 127 | for (var i=0; i<matches.length; ++i) { |
|
122 | 128 | select.append($('<option/>').text(matches[i])); |
|
123 | 129 | } |
|
124 | 130 | select.children().first().attr('selected','true'); |
|
125 | 131 | select.attr('size',Math.min(10,matches.length)); |
|
126 | 132 | var pos = this.code_mirror.cursorCoords(); |
|
127 | 133 | complete.css('left',pos.x+'px'); |
|
128 | 134 | complete.css('top',pos.yBot+'px'); |
|
129 | 135 | complete.append(select); |
|
130 | 136 | |
|
131 | 137 | $('body').append(complete); |
|
132 | 138 | var done = false; |
|
133 | 139 | |
|
134 | 140 | var close = function () { |
|
135 | 141 | if (done) return; |
|
136 | 142 | done = true; |
|
137 | 143 | complete.remove(); |
|
138 | 144 | that.is_completing = false; |
|
139 | 145 | that.completion_cursor = null; |
|
140 | 146 | }; |
|
141 | 147 | |
|
142 | 148 | var pick = function () { |
|
143 | 149 | insert(select.val()[0]); |
|
144 | 150 | close(); |
|
145 | 151 | setTimeout(function(){that.code_mirror.focus();}, 50); |
|
146 | 152 | }; |
|
147 | 153 | |
|
148 | 154 | select.blur(close); |
|
149 | 155 | select.keydown(function (event) { |
|
150 | 156 | var code = event.which; |
|
151 | 157 | if (code === 13 || code === 32) { |
|
152 | 158 | // Pressing SPACE or ENTER will cause a pick |
|
153 | 159 | event.stopPropagation(); |
|
154 | 160 | event.preventDefault(); |
|
155 | 161 | pick(); |
|
156 | 162 | } else if (code === 38 || code === 40) { |
|
157 | 163 | // We don't want the document keydown handler to handle UP/DOWN, |
|
158 | 164 | // but we want the default action. |
|
159 | 165 | event.stopPropagation(); |
|
160 | 166 | } else { |
|
161 | 167 | // All other key presses exit completion. |
|
162 | 168 | event.stopPropagation(); |
|
163 | 169 | event.preventDefault(); |
|
164 | 170 | close(); |
|
165 | 171 | that.code_mirror.focus(); |
|
166 | 172 | } |
|
167 | 173 | }); |
|
168 | 174 | // Double click also causes a pick. |
|
169 | 175 | select.dblclick(pick); |
|
170 | 176 | select.focus(); |
|
171 | 177 | }; |
|
172 | 178 | |
|
173 | 179 | |
|
174 | 180 | CodeCell.prototype.select = function () { |
|
175 | 181 | IPython.Cell.prototype.select.apply(this); |
|
176 | 182 | // Todo: this dance is needed because as of CodeMirror 2.12, focus is |
|
177 | 183 | // not causing the cursor to blink if the editor is empty initially. |
|
178 | 184 | // While this seems to fix the issue, this should be fixed |
|
179 | 185 | // in CodeMirror proper. |
|
180 | 186 | var s = this.code_mirror.getValue(); |
|
181 | 187 | if (s === '') this.code_mirror.setValue('.'); |
|
182 | 188 | this.code_mirror.focus(); |
|
183 | 189 | if (s === '') this.code_mirror.setValue(''); |
|
184 | 190 | }; |
|
185 | 191 | |
|
186 | 192 | |
|
187 | 193 | CodeCell.prototype.append_output = function (json) { |
|
188 | 194 | this.expand(); |
|
189 | 195 | if (json.output_type === 'pyout') { |
|
190 | 196 | this.append_pyout(json); |
|
191 | 197 | } else if (json.output_type === 'pyerr') { |
|
192 | 198 | this.append_pyerr(json); |
|
193 | 199 | } else if (json.output_type === 'display_data') { |
|
194 | 200 | this.append_display_data(json); |
|
195 | 201 | } else if (json.output_type === 'stream') { |
|
196 | 202 | this.append_stream(json); |
|
197 | 203 | }; |
|
198 | 204 | this.outputs.push(json); |
|
199 | 205 | }; |
|
200 | 206 | |
|
201 | 207 | |
|
202 | 208 | CodeCell.prototype.append_pyout = function (json) { |
|
203 | 209 | n = json.prompt_number || ' '; |
|
204 | 210 | var toinsert = $("<div/>").addClass("output_pyout hbox"); |
|
205 | 211 | toinsert.append($('<div/>'). |
|
206 | 212 | addClass('prompt output_prompt'). |
|
207 | 213 | html('Out[' + n + ']:') |
|
208 | 214 | ); |
|
209 | 215 | this.append_mime_type(json, toinsert).addClass('output_area'); |
|
210 | 216 | toinsert.children().last().addClass("box_flex1 pyout_area"); |
|
211 | 217 | this.element.find("div.output").append(toinsert); |
|
212 | 218 | // If we just output latex, typeset it. |
|
213 | 219 | if (json.latex !== undefined) { |
|
214 | 220 | MathJax.Hub.Queue(["Typeset",MathJax.Hub]); |
|
215 | 221 | }; |
|
216 | 222 | }; |
|
217 | 223 | |
|
218 | 224 | |
|
219 | 225 | CodeCell.prototype.append_pyerr = function (json) { |
|
220 | 226 | var tb = json.traceback; |
|
221 | 227 | if (tb !== undefined && tb.length > 0) { |
|
222 | 228 | var s = ''; |
|
223 | 229 | var len = tb.length; |
|
224 | 230 | for (var i=0; i<len; i++) { |
|
225 | 231 | s = s + tb[i] + '\n'; |
|
226 | 232 | } |
|
227 | 233 | s = s + '\n'; |
|
228 | 234 | this.append_text(s).addClass('output_area'); |
|
229 | 235 | }; |
|
230 | 236 | }; |
|
231 | 237 | |
|
232 | 238 | |
|
233 | 239 | CodeCell.prototype.append_stream = function (json) { |
|
234 | 240 | this.append_text(json.text).addClass('output_area'); |
|
235 | 241 | }; |
|
236 | 242 | |
|
237 | 243 | |
|
238 | 244 | CodeCell.prototype.append_display_data = function (json) { |
|
239 | 245 | this.append_mime_type(json).addClass('output_area'); |
|
240 | 246 | // If we just output latex, typeset it. |
|
241 | 247 | if (json.latex !== undefined) { |
|
242 | 248 | MathJax.Hub.Queue(["Typeset",MathJax.Hub]); |
|
243 | 249 | }; |
|
244 | 250 | }; |
|
245 | 251 | |
|
246 | 252 | |
|
247 | 253 | CodeCell.prototype.append_mime_type = function (json, element) { |
|
248 | 254 | element = element || this.element.find("div.output"); |
|
249 | 255 | if (json.html !== undefined) { |
|
250 | 256 | this.append_html(json.html, element); |
|
251 | 257 | } else if (json.latex !== undefined) { |
|
252 | 258 | this.append_latex(json.latex, element); |
|
253 | 259 | } else if (json.svg !== undefined) { |
|
254 | 260 | this.append_svg(json.svg, element); |
|
255 | 261 | } else if (json.png !== undefined) { |
|
256 | 262 | this.append_png(json.png, element); |
|
257 | 263 | } else if (json.jpeg !== undefined) { |
|
258 | 264 | this.append_jpeg(json.jpeg, element); |
|
259 | 265 | } else if (json.text !== undefined) { |
|
260 | 266 | this.append_text(json.text, element); |
|
261 | 267 | }; |
|
262 | 268 | return element; |
|
263 | 269 | }; |
|
264 | 270 | |
|
265 | 271 | |
|
266 | 272 | CodeCell.prototype.append_html = function (html, element) { |
|
267 | 273 | element = element || this.element.find("div.output"); |
|
268 | 274 | var toinsert = $("<div/>").addClass("output_html rendered_html"); |
|
269 | 275 | toinsert.append(html); |
|
270 | 276 | element.append(toinsert); |
|
271 | 277 | return element; |
|
272 | 278 | } |
|
273 | 279 | |
|
274 | 280 | |
|
275 | 281 | CodeCell.prototype.append_text = function (data, element) { |
|
276 | 282 | element = element || this.element.find("div.output"); |
|
277 | 283 | var toinsert = $("<div/>").addClass("output_stream"); |
|
278 | 284 | toinsert.append($("<pre/>").html(data)); |
|
279 | 285 | element.append(toinsert); |
|
280 | 286 | return element; |
|
281 | 287 | }; |
|
282 | 288 | |
|
283 | 289 | |
|
284 | 290 | CodeCell.prototype.append_svg = function (svg, element) { |
|
285 | 291 | element = element || this.element.find("div.output"); |
|
286 | 292 | var toinsert = $("<div/>").addClass("output_svg"); |
|
287 | 293 | toinsert.append(svg); |
|
288 | 294 | element.append(toinsert); |
|
289 | 295 | return element; |
|
290 | 296 | }; |
|
291 | 297 | |
|
292 | 298 | |
|
293 | 299 | CodeCell.prototype.append_png = function (png, element) { |
|
294 | 300 | element = element || this.element.find("div.output"); |
|
295 | 301 | var toinsert = $("<div/>").addClass("output_png"); |
|
296 | 302 | toinsert.append($("<img/>").attr('src','data:image/png;base64,'+png)); |
|
297 | 303 | element.append(toinsert); |
|
298 | 304 | return element; |
|
299 | 305 | }; |
|
300 | 306 | |
|
301 | 307 | |
|
302 | 308 | CodeCell.prototype.append_jpeg = function (jpeg, element) { |
|
303 | 309 | element = element || this.element.find("div.output"); |
|
304 | 310 | var toinsert = $("<div/>").addClass("output_jpeg"); |
|
305 | 311 | toinsert.append($("<img/>").attr('src','data:image/jpeg;base64,'+jpeg)); |
|
306 | 312 | element.append(toinsert); |
|
307 | 313 | return element; |
|
308 | 314 | }; |
|
309 | 315 | |
|
310 | 316 | |
|
311 | 317 | CodeCell.prototype.append_latex = function (latex, element) { |
|
312 | 318 | // This method cannot do the typesetting because the latex first has to |
|
313 | 319 | // be on the page. |
|
314 | 320 | element = element || this.element.find("div.output"); |
|
315 | 321 | var toinsert = $("<div/>").addClass("output_latex"); |
|
316 | 322 | toinsert.append(latex); |
|
317 | 323 | element.append(toinsert); |
|
318 | 324 | return element; |
|
319 | 325 | } |
|
320 | 326 | |
|
321 | 327 | |
|
322 | 328 | CodeCell.prototype.clear_output = function () { |
|
323 | 329 | this.element.find("div.output").html(""); |
|
324 | 330 | this.outputs = []; |
|
325 | 331 | }; |
|
326 | 332 | |
|
327 | 333 | |
|
328 | 334 | CodeCell.prototype.clear_input = function () { |
|
329 | 335 | this.code_mirror.setValue(''); |
|
330 | 336 | }; |
|
331 | 337 | |
|
332 | 338 | |
|
333 | 339 | CodeCell.prototype.collapse = function () { |
|
334 | 340 | if (!this.collapsed) { |
|
335 | 341 | this.element.find('div.output').hide(); |
|
336 | 342 | this.collapsed = true; |
|
337 | 343 | }; |
|
338 | 344 | }; |
|
339 | 345 | |
|
340 | 346 | |
|
341 | 347 | CodeCell.prototype.expand = function () { |
|
342 | 348 | if (this.collapsed) { |
|
343 | 349 | this.element.find('div.output').show(); |
|
344 | 350 | this.collapsed = false; |
|
345 | 351 | }; |
|
346 | 352 | }; |
|
347 | 353 | |
|
348 | 354 | |
|
349 | 355 | CodeCell.prototype.set_input_prompt = function (number) { |
|
350 | 356 | var n = number || ' '; |
|
351 | 357 | this.input_prompt_number = n |
|
352 | 358 | this.element.find('div.input_prompt').html('In [' + n + ']:'); |
|
353 | 359 | }; |
|
354 | 360 | |
|
355 | 361 | |
|
356 | 362 | CodeCell.prototype.get_code = function () { |
|
357 | 363 | return this.code_mirror.getValue(); |
|
358 | 364 | }; |
|
359 | 365 | |
|
360 | 366 | |
|
361 | 367 | CodeCell.prototype.set_code = function (code) { |
|
362 | 368 | return this.code_mirror.setValue(code); |
|
363 | 369 | }; |
|
364 | 370 | |
|
365 | 371 | |
|
366 | 372 | CodeCell.prototype.at_top = function () { |
|
367 | 373 | var cursor = this.code_mirror.getCursor(); |
|
368 | 374 | if (cursor.line === 0) { |
|
369 | 375 | return true; |
|
370 | 376 | } else { |
|
371 | 377 | return false; |
|
372 | 378 | } |
|
373 | 379 | }; |
|
374 | 380 | |
|
375 | 381 | |
|
376 | 382 | CodeCell.prototype.at_bottom = function () { |
|
377 | 383 | var cursor = this.code_mirror.getCursor(); |
|
378 | 384 | if (cursor.line === (this.code_mirror.lineCount()-1)) { |
|
379 | 385 | return true; |
|
380 | 386 | } else { |
|
381 | 387 | return false; |
|
382 | 388 | } |
|
383 | 389 | }; |
|
384 | 390 | |
|
385 | 391 | |
|
386 | 392 | CodeCell.prototype.fromJSON = function (data) { |
|
387 | 393 | // console.log('Import from JSON:', data); |
|
388 | 394 | if (data.cell_type === 'code') { |
|
389 | 395 | if (data.input !== undefined) { |
|
390 | 396 | this.set_code(data.input); |
|
391 | 397 | } |
|
392 | 398 | if (data.prompt_number !== undefined) { |
|
393 | 399 | this.set_input_prompt(data.prompt_number); |
|
394 | 400 | } else { |
|
395 | 401 | this.set_input_prompt(); |
|
396 | 402 | }; |
|
397 | 403 | var len = data.outputs.length; |
|
398 | 404 | for (var i=0; i<len; i++) { |
|
399 | 405 | this.append_output(data.outputs[i]); |
|
400 | 406 | }; |
|
401 | 407 | if (data.collapsed !== undefined) { |
|
402 | 408 | if (data.collapsed) { |
|
403 | 409 | this.collapse(); |
|
404 | 410 | }; |
|
405 | 411 | }; |
|
406 | 412 | }; |
|
407 | 413 | }; |
|
408 | 414 | |
|
409 | 415 | |
|
410 | 416 | CodeCell.prototype.toJSON = function () { |
|
411 | 417 | var data = {}; |
|
412 | 418 | data.input = this.get_code(); |
|
413 | 419 | data.cell_type = 'code'; |
|
414 | 420 | if (this.input_prompt_number !== ' ') { |
|
415 | 421 | data.prompt_number = this.input_prompt_number |
|
416 | 422 | }; |
|
417 | 423 | var outputs = []; |
|
418 | 424 | var len = this.outputs.length; |
|
419 | 425 | for (var i=0; i<len; i++) { |
|
420 | 426 | outputs[i] = this.outputs[i]; |
|
421 | 427 | }; |
|
422 | 428 | data.outputs = outputs; |
|
423 | 429 | data.language = 'python'; |
|
424 | 430 | data.collapsed = this.collapsed; |
|
425 | 431 | // console.log('Export to JSON:',data); |
|
426 | 432 | return data; |
|
427 | 433 | }; |
|
428 | 434 | |
|
429 | 435 | |
|
430 | 436 | IPython.CodeCell = CodeCell; |
|
431 | 437 | |
|
432 | 438 | return IPython; |
|
433 | 439 | }(IPython)); |
|
434 | 440 |
@@ -1,143 +1,149 b'' | |||
|
1 | //---------------------------------------------------------------------------- | |
|
2 | // Copyright (C) 2008-2011 The IPython Development Team | |
|
3 | // | |
|
4 | // Distributed under the terms of the BSD License. The full license is in | |
|
5 | // the file COPYING, distributed as part of this software. | |
|
6 | //---------------------------------------------------------------------------- | |
|
1 | 7 |
|
|
2 | 8 | //============================================================================ |
|
3 | 9 | // Kernel |
|
4 | 10 | //============================================================================ |
|
5 | 11 | |
|
6 | 12 | var IPython = (function (IPython) { |
|
7 | 13 | |
|
8 | 14 | var utils = IPython.utils; |
|
9 | 15 | |
|
10 | 16 | var Kernel = function () { |
|
11 | 17 | this.kernel_id = null; |
|
12 | 18 | this.base_url = "/kernels"; |
|
13 | 19 | this.kernel_url = null; |
|
14 | 20 | this.shell_channel = null; |
|
15 | 21 | this.iopub_channel = null; |
|
16 | 22 | this.running = false; |
|
17 | 23 | }; |
|
18 | 24 | |
|
19 | 25 | |
|
20 | 26 | Kernel.prototype.get_msg = function (msg_type, content) { |
|
21 | 27 | var msg = { |
|
22 | 28 | header : { |
|
23 | 29 | msg_id : utils.uuid(), |
|
24 | 30 | username : "username", |
|
25 | 31 | session: this.session_id, |
|
26 | 32 | msg_type : msg_type |
|
27 | 33 | }, |
|
28 | 34 | content : content, |
|
29 | 35 | parent_header : {} |
|
30 | 36 | }; |
|
31 | 37 | return msg; |
|
32 | 38 | } |
|
33 | 39 | |
|
34 | 40 | Kernel.prototype.start = function (notebook_id, callback) { |
|
35 | 41 | var that = this; |
|
36 | 42 | if (!this.running) { |
|
37 | 43 | var qs = $.param({notebook:notebook_id}); |
|
38 | 44 | $.post(this.base_url + '?' + qs, |
|
39 | 45 | function (kernel_id) { |
|
40 | 46 | that._handle_start_kernel(kernel_id, callback); |
|
41 | 47 | }, |
|
42 | 48 | 'json' |
|
43 | 49 | ); |
|
44 | 50 | }; |
|
45 | 51 | }; |
|
46 | 52 | |
|
47 | 53 | |
|
48 | 54 | Kernel.prototype.restart = function (callback) { |
|
49 | 55 | IPython.kernel_status_widget.status_restarting(); |
|
50 | 56 | var url = this.kernel_url + "/restart"; |
|
51 | 57 | var that = this; |
|
52 | 58 | if (this.running) { |
|
53 | 59 | this.stop_channels(); |
|
54 | 60 | $.post(url, |
|
55 | 61 | function (kernel_id) { |
|
56 | 62 | that._handle_start_kernel(kernel_id, callback); |
|
57 | 63 | }, |
|
58 | 64 | 'json' |
|
59 | 65 | ); |
|
60 | 66 | }; |
|
61 | 67 | }; |
|
62 | 68 | |
|
63 | 69 | |
|
64 | 70 | Kernel.prototype._handle_start_kernel = function (json, callback) { |
|
65 | 71 | this.running = true; |
|
66 | 72 | this.kernel_id = json.kernel_id; |
|
67 | 73 | this.ws_url = json.ws_url; |
|
68 | 74 | this.kernel_url = this.base_url + "/" + this.kernel_id; |
|
69 | 75 | this.start_channels(); |
|
70 | 76 | callback(); |
|
71 | 77 | IPython.kernel_status_widget.status_idle(); |
|
72 | 78 | }; |
|
73 | 79 | |
|
74 | 80 | |
|
75 | 81 | Kernel.prototype.start_channels = function () { |
|
76 | 82 | this.stop_channels(); |
|
77 | 83 | var ws_url = this.ws_url + this.kernel_url; |
|
78 | 84 | console.log("Starting WS:", ws_url); |
|
79 | 85 | this.shell_channel = new WebSocket(ws_url + "/shell"); |
|
80 | 86 | this.iopub_channel = new WebSocket(ws_url + "/iopub"); |
|
81 | 87 | }; |
|
82 | 88 | |
|
83 | 89 | |
|
84 | 90 | Kernel.prototype.stop_channels = function () { |
|
85 | 91 | if (this.shell_channel !== null) { |
|
86 | 92 | this.shell_channel.close(); |
|
87 | 93 | this.shell_channel = null; |
|
88 | 94 | }; |
|
89 | 95 | if (this.iopub_channel !== null) { |
|
90 | 96 | this.iopub_channel.close(); |
|
91 | 97 | this.iopub_channel = null; |
|
92 | 98 | }; |
|
93 | 99 | }; |
|
94 | 100 | |
|
95 | 101 | Kernel.prototype.execute = function (code) { |
|
96 | 102 | var content = { |
|
97 | 103 | code : code, |
|
98 | 104 | silent : false, |
|
99 | 105 | user_variables : [], |
|
100 | 106 | user_expressions : {} |
|
101 | 107 | }; |
|
102 | 108 | var msg = this.get_msg("execute_request", content); |
|
103 | 109 | this.shell_channel.send(JSON.stringify(msg)); |
|
104 | 110 | return msg.header.msg_id; |
|
105 | 111 | } |
|
106 | 112 | |
|
107 | 113 | |
|
108 | 114 | Kernel.prototype.complete = function (line, cursor_pos) { |
|
109 | 115 | var content = { |
|
110 | 116 | text : '', |
|
111 | 117 | line : line, |
|
112 | 118 | cursor_pos : cursor_pos |
|
113 | 119 | }; |
|
114 | 120 | var msg = this.get_msg("complete_request", content); |
|
115 | 121 | this.shell_channel.send(JSON.stringify(msg)); |
|
116 | 122 | return msg.header.msg_id; |
|
117 | 123 | } |
|
118 | 124 | |
|
119 | 125 | |
|
120 | 126 | Kernel.prototype.interrupt = function () { |
|
121 | 127 | if (this.running) { |
|
122 | 128 | $.post(this.kernel_url + "/interrupt"); |
|
123 | 129 | }; |
|
124 | 130 | }; |
|
125 | 131 | |
|
126 | 132 | |
|
127 | 133 | Kernel.prototype.kill = function () { |
|
128 | 134 | if (this.running) { |
|
129 | 135 | this.running = false; |
|
130 | 136 | var settings = { |
|
131 | 137 | cache : false, |
|
132 | 138 | type : "DELETE", |
|
133 | 139 | }; |
|
134 | 140 | $.ajax(this.kernel_url, settings); |
|
135 | 141 | }; |
|
136 | 142 | }; |
|
137 | 143 | |
|
138 | 144 | IPython.Kernel = Kernel; |
|
139 | 145 | |
|
140 | 146 | return IPython; |
|
141 | 147 | |
|
142 | 148 | }(IPython)); |
|
143 | 149 |
@@ -1,54 +1,60 b'' | |||
|
1 | //---------------------------------------------------------------------------- | |
|
2 | // Copyright (C) 2008-2011 The IPython Development Team | |
|
3 | // | |
|
4 | // Distributed under the terms of the BSD License. The full license is in | |
|
5 | // the file COPYING, distributed as part of this software. | |
|
6 | //---------------------------------------------------------------------------- | |
|
1 | 7 |
|
|
2 | 8 | //============================================================================ |
|
3 | 9 | // Kernel Status widget |
|
4 | 10 | //============================================================================ |
|
5 | 11 | |
|
6 | 12 | var IPython = (function (IPython) { |
|
7 | 13 | |
|
8 | 14 | var utils = IPython.utils; |
|
9 | 15 | |
|
10 | 16 | var KernelStatusWidget = function (selector) { |
|
11 | 17 | this.selector = selector; |
|
12 | 18 | if (this.selector !== undefined) { |
|
13 | 19 | this.element = $(selector); |
|
14 | 20 | this.style(); |
|
15 | 21 | } |
|
16 | 22 | }; |
|
17 | 23 | |
|
18 | 24 | |
|
19 | 25 | KernelStatusWidget.prototype.style = function () { |
|
20 | 26 | this.element.addClass('ui-widget'); |
|
21 | 27 | }; |
|
22 | 28 | |
|
23 | 29 | |
|
24 | 30 | KernelStatusWidget.prototype.status_busy = function () { |
|
25 | 31 | this.element.removeClass("status_idle"); |
|
26 | 32 | this.element.removeClass("status_restarting"); |
|
27 | 33 | this.element.addClass("status_busy"); |
|
28 | 34 | this.element.text("Busy"); |
|
29 | 35 | }; |
|
30 | 36 | |
|
31 | 37 | |
|
32 | 38 | KernelStatusWidget.prototype.status_idle = function () { |
|
33 | 39 | this.element.removeClass("status_busy"); |
|
34 | 40 | this.element.removeClass("status_restarting"); |
|
35 | 41 | this.element.addClass("status_idle"); |
|
36 | 42 | this.element.text("Idle"); |
|
37 | 43 | }; |
|
38 | 44 | |
|
39 | 45 | KernelStatusWidget.prototype.status_restarting = function () { |
|
40 | 46 | this.element.removeClass("status_busy"); |
|
41 | 47 | this.element.removeClass("status_idle"); |
|
42 | 48 | this.element.addClass("status_restarting"); |
|
43 | 49 | this.element.text("Restarting"); |
|
44 | 50 | }; |
|
45 | 51 | |
|
46 | 52 | |
|
47 | 53 | |
|
48 | 54 | |
|
49 | 55 | IPython.KernelStatusWidget = KernelStatusWidget; |
|
50 | 56 | |
|
51 | 57 | return IPython; |
|
52 | 58 | |
|
53 | 59 | }(IPython)); |
|
54 | 60 |
@@ -1,55 +1,61 b'' | |||
|
1 | //---------------------------------------------------------------------------- | |
|
2 | // Copyright (C) 2008-2011 The IPython Development Team | |
|
3 | // | |
|
4 | // Distributed under the terms of the BSD License. The full license is in | |
|
5 | // the file COPYING, distributed as part of this software. | |
|
6 | //---------------------------------------------------------------------------- | |
|
1 | 7 |
|
|
2 | 8 | //============================================================================ |
|
3 | 9 | // Layout |
|
4 | 10 | //============================================================================ |
|
5 | 11 | |
|
6 | 12 | var IPython = (function (IPython) { |
|
7 | 13 | |
|
8 | 14 | var LayoutManager = function () { |
|
9 | 15 | this.bind_events(); |
|
10 | 16 | }; |
|
11 | 17 | |
|
12 | 18 | |
|
13 | 19 | LayoutManager.prototype.bind_events = function () { |
|
14 | 20 | $(window).resize($.proxy(this.do_resize,this)); |
|
15 | 21 | }; |
|
16 | 22 | |
|
17 | 23 | |
|
18 | 24 | LayoutManager.prototype.do_resize = function () { |
|
19 | 25 | var win = $(window); |
|
20 | 26 | var w = win.width(); |
|
21 | 27 | var h = win.height(); |
|
22 | 28 | var header_height = $('div#header').outerHeight(true); |
|
23 | 29 | var app_height = h - header_height - 2; // content height |
|
24 | 30 | |
|
25 | 31 | $('div#main_app').height(app_height + 2); // content+padding+border height |
|
26 | 32 | |
|
27 | 33 | $('div#left_panel').height(app_height); |
|
28 | 34 | |
|
29 | 35 | $('div#left_panel_splitter').height(app_height); |
|
30 | 36 | |
|
31 | 37 | $('div#notebook_panel').height(app_height); |
|
32 | 38 | var left_panel_width = $('div#left_panel').outerWidth(); |
|
33 | 39 | var left_panel_splitter_width = $('div#left_panel_splitter').outerWidth(); |
|
34 | 40 | if (IPython.left_panel.expanded) { |
|
35 | 41 | $('div#notebook_panel').css({marginLeft : left_panel_width+left_panel_splitter_width}); |
|
36 | 42 | } else { |
|
37 | 43 | $('div#notebook_panel').css({marginLeft : left_panel_splitter_width}); |
|
38 | 44 | } |
|
39 | 45 | |
|
40 | 46 | |
|
41 | 47 | var pager_height = IPython.pager.percentage_height*app_height; |
|
42 | 48 | var pager_splitter_height = $('div#pager_splitter').outerHeight(true); |
|
43 | 49 | $('div#pager').height(pager_height); |
|
44 | 50 | if (IPython.pager.expanded) { |
|
45 | 51 | $('div#notebook').height(app_height-pager_height-pager_splitter_height); |
|
46 | 52 | } else { |
|
47 | 53 | $('div#notebook').height(app_height-pager_splitter_height); |
|
48 | 54 | } |
|
49 | 55 | }; |
|
50 | 56 | |
|
51 | 57 | IPython.LayoutManager = LayoutManager |
|
52 | 58 | |
|
53 | 59 | return IPython; |
|
54 | 60 | |
|
55 | 61 | }(IPython)); |
@@ -1,95 +1,101 b'' | |||
|
1 | //---------------------------------------------------------------------------- | |
|
2 | // Copyright (C) 2008-2011 The IPython Development Team | |
|
3 | // | |
|
4 | // Distributed under the terms of the BSD License. The full license is in | |
|
5 | // the file COPYING, distributed as part of this software. | |
|
6 | //---------------------------------------------------------------------------- | |
|
1 | 7 |
|
|
2 | 8 | //============================================================================ |
|
3 | 9 | // LeftPanel |
|
4 | 10 | //============================================================================ |
|
5 | 11 | |
|
6 | 12 | |
|
7 | 13 | var IPython = (function (IPython) { |
|
8 | 14 | |
|
9 | 15 | var utils = IPython.utils; |
|
10 | 16 | |
|
11 | 17 | var LeftPanel = function (left_panel_selector, left_panel_splitter_selector) { |
|
12 | 18 | this.left_panel_element = $(left_panel_selector); |
|
13 | 19 | this.left_panel_splitter_element = $(left_panel_splitter_selector); |
|
14 | 20 | this.expanded = true; |
|
15 | 21 | this.width = 300; |
|
16 | 22 | this.style(); |
|
17 | 23 | this.bind_events(); |
|
18 | 24 | this.create_children(); |
|
19 | 25 | }; |
|
20 | 26 | |
|
21 | 27 | |
|
22 | 28 | LeftPanel.prototype.style = function () { |
|
23 | 29 | this.left_panel_splitter_element.addClass('border-box-sizing ui-widget ui-state-default'); |
|
24 | 30 | this.left_panel_element.addClass('border-box-sizing ui-widget'); |
|
25 | 31 | this.left_panel_element.width(this.width); |
|
26 | 32 | this.left_panel_splitter_element.css({left : this.width}); |
|
27 | 33 | }; |
|
28 | 34 | |
|
29 | 35 | |
|
30 | 36 | LeftPanel.prototype.bind_events = function () { |
|
31 | 37 | var that = this; |
|
32 | 38 | |
|
33 | 39 | this.left_panel_element.bind('collapse_left_panel', function () { |
|
34 | 40 | that.left_panel_element.hide('fast'); |
|
35 | 41 | that.left_panel_splitter_element.animate({left : 0}, 'fast'); |
|
36 | 42 | }); |
|
37 | 43 | |
|
38 | 44 | this.left_panel_element.bind('expand_left_panel', function () { |
|
39 | 45 | that.left_panel_element.show('fast'); |
|
40 | 46 | that.left_panel_splitter_element.animate({left : that.width}, 'fast'); |
|
41 | 47 | }); |
|
42 | 48 | |
|
43 | 49 | this.left_panel_splitter_element.hover( |
|
44 | 50 | function () { |
|
45 | 51 | that.left_panel_splitter_element.addClass('ui-state-hover'); |
|
46 | 52 | }, |
|
47 | 53 | function () { |
|
48 | 54 | that.left_panel_splitter_element.removeClass('ui-state-hover'); |
|
49 | 55 | } |
|
50 | 56 | ); |
|
51 | 57 | |
|
52 | 58 | this.left_panel_splitter_element.click(function () { |
|
53 | 59 | that.toggle(); |
|
54 | 60 | }); |
|
55 | 61 | |
|
56 | 62 | }; |
|
57 | 63 | |
|
58 | 64 | |
|
59 | 65 | LeftPanel.prototype.create_children = function () { |
|
60 | 66 | this.notebook_section = new IPython.NotebookSection('div#notebook_section'); |
|
61 | 67 | this.cell_section = new IPython.CellSection('div#cell_section'); |
|
62 | 68 | this.kernel_section = new IPython.KernelSection('div#kernel_section'); |
|
63 | 69 | this.help_section = new IPython.HelpSection('div#help_section'); |
|
64 | 70 | } |
|
65 | 71 | |
|
66 | 72 | LeftPanel.prototype.collapse = function () { |
|
67 | 73 | if (this.expanded === true) { |
|
68 | 74 | this.left_panel_element.add($('div#notebook')).trigger('collapse_left_panel'); |
|
69 | 75 | this.expanded = false; |
|
70 | 76 | }; |
|
71 | 77 | }; |
|
72 | 78 | |
|
73 | 79 | |
|
74 | 80 | LeftPanel.prototype.expand = function () { |
|
75 | 81 | if (this.expanded !== true) { |
|
76 | 82 | this.left_panel_element.add($('div#notebook')).trigger('expand_left_panel'); |
|
77 | 83 | this.expanded = true; |
|
78 | 84 | }; |
|
79 | 85 | }; |
|
80 | 86 | |
|
81 | 87 | |
|
82 | 88 | LeftPanel.prototype.toggle = function () { |
|
83 | 89 | if (this.expanded === true) { |
|
84 | 90 | this.collapse(); |
|
85 | 91 | } else { |
|
86 | 92 | this.expand(); |
|
87 | 93 | }; |
|
88 | 94 | }; |
|
89 | 95 | |
|
90 | 96 | IPython.LeftPanel = LeftPanel; |
|
91 | 97 | |
|
92 | 98 | return IPython; |
|
93 | 99 | |
|
94 | 100 | }(IPython)); |
|
95 | 101 |
@@ -1,23 +1,30 b'' | |||
|
1 | //---------------------------------------------------------------------------- | |
|
2 | // Copyright (C) 2008-2011 The IPython Development Team | |
|
3 | // | |
|
4 | // Distributed under the terms of the BSD License. The full license is in | |
|
5 | // the file COPYING, distributed as part of this software. | |
|
6 | //---------------------------------------------------------------------------- | |
|
7 | ||
|
1 | 8 | var IPython = IPython || {}; |
|
2 | 9 | |
|
3 | 10 | IPython.namespace = function (ns_string) { |
|
4 | 11 | var parts = ns_string.split('.'), |
|
5 | 12 | parent = IPython, |
|
6 | 13 | i; |
|
7 | 14 | |
|
8 | 15 | // String redundant leading global |
|
9 | 16 | if (parts[0] === "IPython") { |
|
10 | 17 | parts = parts.slice(1); |
|
11 | 18 | } |
|
12 | 19 | |
|
13 | 20 | for (i=0; i<parts.length; i+=1) { |
|
14 | 21 | // Create property if it doesn't exist |
|
15 | 22 | if (typeof parent[parts[i]] === "undefined") { |
|
16 | 23 | parent[parts[i]] == {}; |
|
17 | 24 | } |
|
18 | 25 | } |
|
19 | 26 | return parent; |
|
20 | 27 | }; |
|
21 | 28 | |
|
22 | 29 | |
|
23 | 30 |
@@ -1,33 +1,39 b'' | |||
|
1 | //---------------------------------------------------------------------------- | |
|
2 | // Copyright (C) 2008-2011 The IPython Development Team | |
|
3 | // | |
|
4 | // Distributed under the terms of the BSD License. The full license is in | |
|
5 | // the file COPYING, distributed as part of this software. | |
|
6 | //---------------------------------------------------------------------------- | |
|
1 | 7 |
|
|
2 | 8 | //============================================================================ |
|
3 | 9 | // On document ready |
|
4 | 10 | //============================================================================ |
|
5 | 11 | |
|
6 | 12 | |
|
7 | 13 | $(document).ready(function () { |
|
8 | 14 | |
|
9 | 15 | $('div#header').addClass('border-box-sizing'); |
|
10 | 16 | $('div#header_border').addClass('border-box-sizing ui-widget ui-widget-content'); |
|
11 | 17 | |
|
12 | 18 | $('div#main_app').addClass('border-box-sizing ui-widget'); |
|
13 | 19 | $('div#app_hbox').addClass('hbox'); |
|
14 | 20 | |
|
15 | 21 | $('div#content_toolbar').addClass('ui-widget ui-helper-clearfix'); |
|
16 | 22 | |
|
17 | 23 | $('#new_notebook').button().click(function (e) { |
|
18 | 24 | window.open('/new'); |
|
19 | 25 | }); |
|
20 | 26 | |
|
21 | 27 | $('div#left_panel').addClass('box-flex'); |
|
22 | 28 | $('div#right_panel').addClass('box-flex'); |
|
23 | 29 | |
|
24 | 30 | IPython.notebook_list = new IPython.NotebookList('div#notebook_list'); |
|
25 | 31 | IPython.notebook_list.load_list(); |
|
26 | 32 | |
|
27 | 33 | // These have display: none in the css file and are made visible here to prevent FLOUC. |
|
28 | 34 | $('div#header').css('display','block'); |
|
29 | 35 | $('div#main_app').css('display','block'); |
|
30 | 36 | |
|
31 | 37 | |
|
32 | 38 | }); |
|
33 | 39 |
@@ -1,813 +1,819 b'' | |||
|
1 | //---------------------------------------------------------------------------- | |
|
2 | // Copyright (C) 2008-2011 The IPython Development Team | |
|
3 | // | |
|
4 | // Distributed under the terms of the BSD License. The full license is in | |
|
5 | // the file COPYING, distributed as part of this software. | |
|
6 | //---------------------------------------------------------------------------- | |
|
1 | 7 |
|
|
2 | 8 | //============================================================================ |
|
3 | 9 | // Notebook |
|
4 | 10 | //============================================================================ |
|
5 | 11 | |
|
6 | 12 | var IPython = (function (IPython) { |
|
7 | 13 | |
|
8 | 14 | var utils = IPython.utils; |
|
9 | 15 | |
|
10 | 16 | var Notebook = function (selector) { |
|
11 | 17 | this.element = $(selector); |
|
12 | 18 | this.element.scroll(); |
|
13 | 19 | this.element.data("notebook", this); |
|
14 | 20 | this.next_prompt_number = 1; |
|
15 | 21 | this.kernel = null; |
|
16 | 22 | this.dirty = false; |
|
17 | 23 | this.msg_cell_map = {}; |
|
18 | 24 | this.style(); |
|
19 | 25 | this.create_elements(); |
|
20 | 26 | this.bind_events(); |
|
21 | 27 | }; |
|
22 | 28 | |
|
23 | 29 | |
|
24 | 30 | Notebook.prototype.style = function () { |
|
25 | 31 | $('div#notebook').addClass('border-box-sizing'); |
|
26 | 32 | }; |
|
27 | 33 | |
|
28 | 34 | |
|
29 | 35 | Notebook.prototype.create_elements = function () { |
|
30 | 36 | // We add this end_space div to the end of the notebook div to: |
|
31 | 37 | // i) provide a margin between the last cell and the end of the notebook |
|
32 | 38 | // ii) to prevent the div from scrolling up when the last cell is being |
|
33 | 39 | // edited, but is too low on the page, which browsers will do automatically. |
|
34 | 40 | var end_space = $('<div class="end_space"></div>').height(150); |
|
35 | 41 | this.element.append(end_space); |
|
36 | 42 | $('div#notebook').addClass('border-box-sizing'); |
|
37 | 43 | }; |
|
38 | 44 | |
|
39 | 45 | |
|
40 | 46 | Notebook.prototype.bind_events = function () { |
|
41 | 47 | var that = this; |
|
42 | 48 | $(document).keydown(function (event) { |
|
43 | 49 | // console.log(event); |
|
44 | 50 | if (event.which === 38) { |
|
45 | 51 | var cell = that.selected_cell(); |
|
46 | 52 | if (cell.at_top()) { |
|
47 | 53 | event.preventDefault(); |
|
48 | 54 | that.select_prev(); |
|
49 | 55 | }; |
|
50 | 56 | } else if (event.which === 40) { |
|
51 | 57 | var cell = that.selected_cell(); |
|
52 | 58 | if (cell.at_bottom()) { |
|
53 | 59 | event.preventDefault(); |
|
54 | 60 | that.select_next(); |
|
55 | 61 | }; |
|
56 | 62 | } else if (event.which === 13 && event.shiftKey) { |
|
57 | 63 | that.execute_selected_cell(); |
|
58 | 64 | return false; |
|
59 | 65 | } else if (event.which === 13 && event.ctrlKey) { |
|
60 | 66 | that.execute_selected_cell({terminal:true}); |
|
61 | 67 | return false; |
|
62 | 68 | }; |
|
63 | 69 | }); |
|
64 | 70 | |
|
65 | 71 | this.element.bind('collapse_pager', function () { |
|
66 | 72 | var app_height = $('div#main_app').height(); // content height |
|
67 | 73 | var splitter_height = $('div#pager_splitter').outerHeight(true); |
|
68 | 74 | var new_height = app_height - splitter_height; |
|
69 | 75 | that.element.animate({height : new_height + 'px'}, 'fast'); |
|
70 | 76 | }); |
|
71 | 77 | |
|
72 | 78 | this.element.bind('expand_pager', function () { |
|
73 | 79 | var app_height = $('div#main_app').height(); // content height |
|
74 | 80 | var splitter_height = $('div#pager_splitter').outerHeight(true); |
|
75 | 81 | var pager_height = $('div#pager').outerHeight(true); |
|
76 | 82 | var new_height = app_height - pager_height - splitter_height; |
|
77 | 83 | that.element.animate({height : new_height + 'px'}, 'fast'); |
|
78 | 84 | }); |
|
79 | 85 | |
|
80 | 86 | this.element.bind('collapse_left_panel', function () { |
|
81 | 87 | var splitter_width = $('div#left_panel_splitter').outerWidth(true); |
|
82 | 88 | var new_margin = splitter_width; |
|
83 | 89 | $('div#notebook_panel').animate({marginLeft : new_margin + 'px'}, 'fast'); |
|
84 | 90 | }); |
|
85 | 91 | |
|
86 | 92 | this.element.bind('expand_left_panel', function () { |
|
87 | 93 | var splitter_width = $('div#left_panel_splitter').outerWidth(true); |
|
88 | 94 | var left_panel_width = IPython.left_panel.width; |
|
89 | 95 | var new_margin = splitter_width + left_panel_width; |
|
90 | 96 | $('div#notebook_panel').animate({marginLeft : new_margin + 'px'}, 'fast'); |
|
91 | 97 | }); |
|
92 | 98 | |
|
93 | 99 | $(window).bind('beforeunload', function () { |
|
94 | 100 | var kill_kernel = $('#kill_kernel').prop('checked'); |
|
95 | 101 | if (kill_kernel) { |
|
96 | 102 | that.kernel.kill(); |
|
97 | 103 | } |
|
98 | 104 | if (that.dirty) { |
|
99 | 105 | return "You have unsaved changes that will be lost if you leave this page."; |
|
100 | 106 | }; |
|
101 | 107 | }); |
|
102 | 108 | }; |
|
103 | 109 | |
|
104 | 110 | |
|
105 | 111 | Notebook.prototype.scroll_to_bottom = function () { |
|
106 | 112 | this.element.animate({scrollTop:this.element.get(0).scrollHeight}, 0); |
|
107 | 113 | }; |
|
108 | 114 | |
|
109 | 115 | |
|
110 | 116 | Notebook.prototype.scroll_to_top = function () { |
|
111 | 117 | this.element.animate({scrollTop:0}, 0); |
|
112 | 118 | }; |
|
113 | 119 | |
|
114 | 120 | |
|
115 | 121 | // Cell indexing, retrieval, etc. |
|
116 | 122 | |
|
117 | 123 | |
|
118 | 124 | Notebook.prototype.cell_elements = function () { |
|
119 | 125 | return this.element.children("div.cell"); |
|
120 | 126 | } |
|
121 | 127 | |
|
122 | 128 | |
|
123 | 129 | Notebook.prototype.ncells = function (cell) { |
|
124 | 130 | return this.cell_elements().length; |
|
125 | 131 | } |
|
126 | 132 | |
|
127 | 133 | |
|
128 | 134 | // TODO: we are often calling cells as cells()[i], which we should optimize |
|
129 | 135 | // to cells(i) or a new method. |
|
130 | 136 | Notebook.prototype.cells = function () { |
|
131 | 137 | return this.cell_elements().toArray().map(function (e) { |
|
132 | 138 | return $(e).data("cell"); |
|
133 | 139 | }); |
|
134 | 140 | } |
|
135 | 141 | |
|
136 | 142 | |
|
137 | 143 | Notebook.prototype.find_cell_index = function (cell) { |
|
138 | 144 | var result = null; |
|
139 | 145 | this.cell_elements().filter(function (index) { |
|
140 | 146 | if ($(this).data("cell") === cell) { |
|
141 | 147 | result = index; |
|
142 | 148 | }; |
|
143 | 149 | }); |
|
144 | 150 | return result; |
|
145 | 151 | }; |
|
146 | 152 | |
|
147 | 153 | |
|
148 | 154 | Notebook.prototype.index_or_selected = function (index) { |
|
149 | 155 | return index || this.selected_index() || 0; |
|
150 | 156 | } |
|
151 | 157 | |
|
152 | 158 | |
|
153 | 159 | Notebook.prototype.select = function (index) { |
|
154 | 160 | if (index !== undefined && index >= 0 && index < this.ncells()) { |
|
155 | 161 | if (this.selected_index() !== null) { |
|
156 | 162 | this.selected_cell().unselect(); |
|
157 | 163 | }; |
|
158 | 164 | this.cells()[index].select(); |
|
159 | 165 | }; |
|
160 | 166 | return this; |
|
161 | 167 | }; |
|
162 | 168 | |
|
163 | 169 | |
|
164 | 170 | Notebook.prototype.select_next = function () { |
|
165 | 171 | var index = this.selected_index(); |
|
166 | 172 | if (index !== null && index >= 0 && (index+1) < this.ncells()) { |
|
167 | 173 | this.select(index+1); |
|
168 | 174 | }; |
|
169 | 175 | return this; |
|
170 | 176 | }; |
|
171 | 177 | |
|
172 | 178 | |
|
173 | 179 | Notebook.prototype.select_prev = function () { |
|
174 | 180 | var index = this.selected_index(); |
|
175 | 181 | if (index !== null && index >= 0 && (index-1) < this.ncells()) { |
|
176 | 182 | this.select(index-1); |
|
177 | 183 | }; |
|
178 | 184 | return this; |
|
179 | 185 | }; |
|
180 | 186 | |
|
181 | 187 | |
|
182 | 188 | Notebook.prototype.selected_index = function () { |
|
183 | 189 | var result = null; |
|
184 | 190 | this.cell_elements().filter(function (index) { |
|
185 | 191 | if ($(this).data("cell").selected === true) { |
|
186 | 192 | result = index; |
|
187 | 193 | }; |
|
188 | 194 | }); |
|
189 | 195 | return result; |
|
190 | 196 | }; |
|
191 | 197 | |
|
192 | 198 | |
|
193 | 199 | Notebook.prototype.cell_for_msg = function (msg_id) { |
|
194 | 200 | var cell_id = this.msg_cell_map[msg_id]; |
|
195 | 201 | var result = null; |
|
196 | 202 | this.cell_elements().filter(function (index) { |
|
197 | 203 | cell = $(this).data("cell"); |
|
198 | 204 | if (cell.cell_id === cell_id) { |
|
199 | 205 | result = cell; |
|
200 | 206 | }; |
|
201 | 207 | }); |
|
202 | 208 | return result; |
|
203 | 209 | }; |
|
204 | 210 | |
|
205 | 211 | |
|
206 | 212 | Notebook.prototype.selected_cell = function () { |
|
207 | 213 | return this.cell_elements().eq(this.selected_index()).data("cell"); |
|
208 | 214 | } |
|
209 | 215 | |
|
210 | 216 | |
|
211 | 217 | // Cell insertion, deletion and moving. |
|
212 | 218 | |
|
213 | 219 | |
|
214 | 220 | Notebook.prototype.delete_cell = function (index) { |
|
215 | 221 | var i = index || this.selected_index(); |
|
216 | 222 | if (i !== null && i >= 0 && i < this.ncells()) { |
|
217 | 223 | this.cell_elements().eq(i).remove(); |
|
218 | 224 | if (i === (this.ncells())) { |
|
219 | 225 | this.select(i-1); |
|
220 | 226 | } else { |
|
221 | 227 | this.select(i); |
|
222 | 228 | }; |
|
223 | 229 | }; |
|
224 | 230 | this.dirty = true; |
|
225 | 231 | return this; |
|
226 | 232 | }; |
|
227 | 233 | |
|
228 | 234 | |
|
229 | 235 | Notebook.prototype.append_cell = function (cell) { |
|
230 | 236 | this.element.find('div.end_space').before(cell.element); |
|
231 | 237 | this.dirty = true; |
|
232 | 238 | return this; |
|
233 | 239 | }; |
|
234 | 240 | |
|
235 | 241 | |
|
236 | 242 | Notebook.prototype.insert_cell_after = function (cell, index) { |
|
237 | 243 | var ncells = this.ncells(); |
|
238 | 244 | if (ncells === 0) { |
|
239 | 245 | this.append_cell(cell); |
|
240 | 246 | return this; |
|
241 | 247 | }; |
|
242 | 248 | if (index >= 0 && index < ncells) { |
|
243 | 249 | this.cell_elements().eq(index).after(cell.element); |
|
244 | 250 | }; |
|
245 | 251 | this.dirty = true; |
|
246 | 252 | return this |
|
247 | 253 | }; |
|
248 | 254 | |
|
249 | 255 | |
|
250 | 256 | Notebook.prototype.insert_cell_before = function (cell, index) { |
|
251 | 257 | var ncells = this.ncells(); |
|
252 | 258 | if (ncells === 0) { |
|
253 | 259 | this.append_cell(cell); |
|
254 | 260 | return this; |
|
255 | 261 | }; |
|
256 | 262 | if (index >= 0 && index < ncells) { |
|
257 | 263 | this.cell_elements().eq(index).before(cell.element); |
|
258 | 264 | }; |
|
259 | 265 | this.dirty = true; |
|
260 | 266 | return this; |
|
261 | 267 | }; |
|
262 | 268 | |
|
263 | 269 | |
|
264 | 270 | Notebook.prototype.move_cell_up = function (index) { |
|
265 | 271 | var i = index || this.selected_index(); |
|
266 | 272 | if (i !== null && i < this.ncells() && i > 0) { |
|
267 | 273 | var pivot = this.cell_elements().eq(i-1); |
|
268 | 274 | var tomove = this.cell_elements().eq(i); |
|
269 | 275 | if (pivot !== null && tomove !== null) { |
|
270 | 276 | tomove.detach(); |
|
271 | 277 | pivot.before(tomove); |
|
272 | 278 | this.select(i-1); |
|
273 | 279 | }; |
|
274 | 280 | }; |
|
275 | 281 | this.dirty = true; |
|
276 | 282 | return this; |
|
277 | 283 | } |
|
278 | 284 | |
|
279 | 285 | |
|
280 | 286 | Notebook.prototype.move_cell_down = function (index) { |
|
281 | 287 | var i = index || this.selected_index(); |
|
282 | 288 | if (i !== null && i < (this.ncells()-1) && i >= 0) { |
|
283 | 289 | var pivot = this.cell_elements().eq(i+1) |
|
284 | 290 | var tomove = this.cell_elements().eq(i) |
|
285 | 291 | if (pivot !== null && tomove !== null) { |
|
286 | 292 | tomove.detach(); |
|
287 | 293 | pivot.after(tomove); |
|
288 | 294 | this.select(i+1); |
|
289 | 295 | }; |
|
290 | 296 | }; |
|
291 | 297 | this.dirty = true; |
|
292 | 298 | return this; |
|
293 | 299 | } |
|
294 | 300 | |
|
295 | 301 | |
|
296 | 302 | Notebook.prototype.sort_cells = function () { |
|
297 | 303 | var ncells = this.ncells(); |
|
298 | 304 | var sindex = this.selected_index(); |
|
299 | 305 | var swapped; |
|
300 | 306 | do { |
|
301 | 307 | swapped = false |
|
302 | 308 | for (var i=1; i<ncells; i++) { |
|
303 | 309 | current = this.cell_elements().eq(i).data("cell"); |
|
304 | 310 | previous = this.cell_elements().eq(i-1).data("cell"); |
|
305 | 311 | if (previous.input_prompt_number > current.input_prompt_number) { |
|
306 | 312 | this.move_cell_up(i); |
|
307 | 313 | swapped = true; |
|
308 | 314 | }; |
|
309 | 315 | }; |
|
310 | 316 | } while (swapped); |
|
311 | 317 | this.select(sindex); |
|
312 | 318 | return this; |
|
313 | 319 | }; |
|
314 | 320 | |
|
315 | 321 | |
|
316 | 322 | Notebook.prototype.insert_code_cell_before = function (index) { |
|
317 | 323 | // TODO: Bounds check for i |
|
318 | 324 | var i = this.index_or_selected(index); |
|
319 | 325 | var cell = new IPython.CodeCell(this); |
|
320 | 326 | cell.set_input_prompt(); |
|
321 | 327 | this.insert_cell_before(cell, i); |
|
322 | 328 | this.select(this.find_cell_index(cell)); |
|
323 | 329 | return cell; |
|
324 | 330 | } |
|
325 | 331 | |
|
326 | 332 | |
|
327 | 333 | Notebook.prototype.insert_code_cell_after = function (index) { |
|
328 | 334 | // TODO: Bounds check for i |
|
329 | 335 | var i = this.index_or_selected(index); |
|
330 | 336 | var cell = new IPython.CodeCell(this); |
|
331 | 337 | cell.set_input_prompt(); |
|
332 | 338 | this.insert_cell_after(cell, i); |
|
333 | 339 | this.select(this.find_cell_index(cell)); |
|
334 | 340 | return cell; |
|
335 | 341 | } |
|
336 | 342 | |
|
337 | 343 | |
|
338 | 344 | Notebook.prototype.insert_html_cell_before = function (index) { |
|
339 | 345 | // TODO: Bounds check for i |
|
340 | 346 | var i = this.index_or_selected(index); |
|
341 | 347 | var cell = new IPython.HTMLCell(this); |
|
342 | 348 | cell.config_mathjax(); |
|
343 | 349 | this.insert_cell_before(cell, i); |
|
344 | 350 | this.select(this.find_cell_index(cell)); |
|
345 | 351 | return cell; |
|
346 | 352 | } |
|
347 | 353 | |
|
348 | 354 | |
|
349 | 355 | Notebook.prototype.insert_html_cell_after = function (index) { |
|
350 | 356 | // TODO: Bounds check for i |
|
351 | 357 | var i = this.index_or_selected(index); |
|
352 | 358 | var cell = new IPython.HTMLCell(this); |
|
353 | 359 | cell.config_mathjax(); |
|
354 | 360 | this.insert_cell_after(cell, i); |
|
355 | 361 | this.select(this.find_cell_index(cell)); |
|
356 | 362 | return cell; |
|
357 | 363 | } |
|
358 | 364 | |
|
359 | 365 | |
|
360 | 366 | Notebook.prototype.insert_markdown_cell_before = function (index) { |
|
361 | 367 | // TODO: Bounds check for i |
|
362 | 368 | var i = this.index_or_selected(index); |
|
363 | 369 | var cell = new IPython.MarkdownCell(this); |
|
364 | 370 | cell.config_mathjax(); |
|
365 | 371 | this.insert_cell_before(cell, i); |
|
366 | 372 | this.select(this.find_cell_index(cell)); |
|
367 | 373 | return cell; |
|
368 | 374 | } |
|
369 | 375 | |
|
370 | 376 | |
|
371 | 377 | Notebook.prototype.insert_markdown_cell_after = function (index) { |
|
372 | 378 | // TODO: Bounds check for i |
|
373 | 379 | var i = this.index_or_selected(index); |
|
374 | 380 | var cell = new IPython.MarkdownCell(this); |
|
375 | 381 | cell.config_mathjax(); |
|
376 | 382 | this.insert_cell_after(cell, i); |
|
377 | 383 | this.select(this.find_cell_index(cell)); |
|
378 | 384 | return cell; |
|
379 | 385 | } |
|
380 | 386 | |
|
381 | 387 | |
|
382 | 388 | Notebook.prototype.to_code = function (index) { |
|
383 | 389 | // TODO: Bounds check for i |
|
384 | 390 | var i = this.index_or_selected(index); |
|
385 | 391 | var source_element = this.cell_elements().eq(i); |
|
386 | 392 | var source_cell = source_element.data("cell"); |
|
387 | 393 | if (source_cell instanceof IPython.HTMLCell || |
|
388 | 394 | source_cell instanceof IPython.MarkdownCell) { |
|
389 | 395 | this.insert_code_cell_after(i); |
|
390 | 396 | var target_cell = this.cells()[i+1]; |
|
391 | 397 | target_cell.set_code(source_cell.get_source()); |
|
392 | 398 | source_element.remove(); |
|
393 | 399 | target_cell.select(); |
|
394 | 400 | }; |
|
395 | 401 | this.dirty = true; |
|
396 | 402 | }; |
|
397 | 403 | |
|
398 | 404 | |
|
399 | 405 | Notebook.prototype.to_markdown = function (index) { |
|
400 | 406 | // TODO: Bounds check for i |
|
401 | 407 | var i = this.index_or_selected(index); |
|
402 | 408 | var source_element = this.cell_elements().eq(i); |
|
403 | 409 | var source_cell = source_element.data("cell"); |
|
404 | 410 | var target_cell = null; |
|
405 | 411 | if (source_cell instanceof IPython.CodeCell) { |
|
406 | 412 | this.insert_markdown_cell_after(i); |
|
407 | 413 | var target_cell = this.cells()[i+1]; |
|
408 | 414 | var text = source_cell.get_code(); |
|
409 | 415 | } else if (source_cell instanceof IPython.HTMLCell) { |
|
410 | 416 | this.insert_markdown_cell_after(i); |
|
411 | 417 | var target_cell = this.cells()[i+1]; |
|
412 | 418 | var text = source_cell.get_source(); |
|
413 | 419 | if (text === source_cell.placeholder) { |
|
414 | 420 | text = target_cell.placeholder; |
|
415 | 421 | } |
|
416 | 422 | } |
|
417 | 423 | if (target_cell !== null) { |
|
418 | 424 | if (text === "") {text = target_cell.placeholder;}; |
|
419 | 425 | target_cell.set_source(text); |
|
420 | 426 | source_element.remove(); |
|
421 | 427 | target_cell.edit(); |
|
422 | 428 | } |
|
423 | 429 | this.dirty = true; |
|
424 | 430 | }; |
|
425 | 431 | |
|
426 | 432 | |
|
427 | 433 | Notebook.prototype.to_html = function (index) { |
|
428 | 434 | // TODO: Bounds check for i |
|
429 | 435 | var i = this.index_or_selected(index); |
|
430 | 436 | var source_element = this.cell_elements().eq(i); |
|
431 | 437 | var source_cell = source_element.data("cell"); |
|
432 | 438 | var target_cell = null; |
|
433 | 439 | if (source_cell instanceof IPython.CodeCell) { |
|
434 | 440 | this.insert_html_cell_after(i); |
|
435 | 441 | var target_cell = this.cells()[i+1]; |
|
436 | 442 | var text = source_cell.get_code(); |
|
437 | 443 | } else if (source_cell instanceof IPython.MarkdownCell) { |
|
438 | 444 | this.insert_html_cell_after(i); |
|
439 | 445 | var target_cell = this.cells()[i+1]; |
|
440 | 446 | var text = source_cell.get_source(); |
|
441 | 447 | if (text === source_cell.placeholder) { |
|
442 | 448 | text = target_cell.placeholder; |
|
443 | 449 | } |
|
444 | 450 | } |
|
445 | 451 | if (target_cell !== null) { |
|
446 | 452 | if (text === "") {text = target_cell.placeholder;}; |
|
447 | 453 | target_cell.set_source(text); |
|
448 | 454 | source_element.remove(); |
|
449 | 455 | target_cell.edit(); |
|
450 | 456 | } |
|
451 | 457 | this.dirty = true; |
|
452 | 458 | }; |
|
453 | 459 | |
|
454 | 460 | |
|
455 | 461 | // Cell collapsing and output clearing |
|
456 | 462 | |
|
457 | 463 | Notebook.prototype.collapse = function (index) { |
|
458 | 464 | var i = this.index_or_selected(index); |
|
459 | 465 | this.cells()[i].collapse(); |
|
460 | 466 | this.dirty = true; |
|
461 | 467 | }; |
|
462 | 468 | |
|
463 | 469 | |
|
464 | 470 | Notebook.prototype.expand = function (index) { |
|
465 | 471 | var i = this.index_or_selected(index); |
|
466 | 472 | this.cells()[i].expand(); |
|
467 | 473 | this.dirty = true; |
|
468 | 474 | }; |
|
469 | 475 | |
|
470 | 476 | |
|
471 | 477 | Notebook.prototype.set_autoindent = function (state) { |
|
472 | 478 | var cells = this.cells(); |
|
473 | 479 | len = cells.length; |
|
474 | 480 | for (var i=0; i<len; i++) { |
|
475 | 481 | cells[i].set_autoindent(state) |
|
476 | 482 | }; |
|
477 | 483 | }; |
|
478 | 484 | |
|
479 | 485 | |
|
480 | 486 | Notebook.prototype.clear_all_output = function () { |
|
481 | 487 | var ncells = this.ncells(); |
|
482 | 488 | var cells = this.cells(); |
|
483 | 489 | for (var i=0; i<ncells; i++) { |
|
484 | 490 | if (cells[i] instanceof IPython.CodeCell) { |
|
485 | 491 | cells[i].clear_output(); |
|
486 | 492 | } |
|
487 | 493 | }; |
|
488 | 494 | this.dirty = true; |
|
489 | 495 | }; |
|
490 | 496 | |
|
491 | 497 | |
|
492 | 498 | // Kernel related things |
|
493 | 499 | |
|
494 | 500 | Notebook.prototype.start_kernel = function () { |
|
495 | 501 | this.kernel = new IPython.Kernel(); |
|
496 | 502 | var notebook_id = IPython.save_widget.get_notebook_id(); |
|
497 | 503 | this.kernel.start(notebook_id, $.proxy(this.kernel_started, this)); |
|
498 | 504 | }; |
|
499 | 505 | |
|
500 | 506 | |
|
501 | 507 | Notebook.prototype.restart_kernel = function () { |
|
502 | 508 | var notebook_id = IPython.save_widget.get_notebook_id(); |
|
503 | 509 | this.kernel.restart($.proxy(this.kernel_started, this)); |
|
504 | 510 | }; |
|
505 | 511 | |
|
506 | 512 | |
|
507 | 513 | Notebook.prototype.kernel_started = function () { |
|
508 | 514 | console.log("Kernel started: ", this.kernel.kernel_id); |
|
509 | 515 | this.kernel.shell_channel.onmessage = $.proxy(this.handle_shell_reply,this); |
|
510 | 516 | this.kernel.iopub_channel.onmessage = $.proxy(this.handle_iopub_reply,this); |
|
511 | 517 | }; |
|
512 | 518 | |
|
513 | 519 | |
|
514 | 520 | Notebook.prototype.handle_shell_reply = function (e) { |
|
515 | 521 | reply = $.parseJSON(e.data); |
|
516 | 522 | var header = reply.header; |
|
517 | 523 | var content = reply.content; |
|
518 | 524 | var msg_type = header.msg_type; |
|
519 | 525 | // console.log(reply); |
|
520 | 526 | var cell = this.cell_for_msg(reply.parent_header.msg_id); |
|
521 | 527 | if (msg_type === "execute_reply") { |
|
522 | 528 | cell.set_input_prompt(content.execution_count); |
|
523 | 529 | this.dirty = true; |
|
524 | 530 | } else if (msg_type === "complete_reply") { |
|
525 | 531 | cell.finish_completing(content.matched_text, content.matches); |
|
526 | 532 | }; |
|
527 | 533 | var payload = content.payload || []; |
|
528 | 534 | this.handle_payload(cell, payload); |
|
529 | 535 | }; |
|
530 | 536 | |
|
531 | 537 | |
|
532 | 538 | Notebook.prototype.handle_payload = function (cell, payload) { |
|
533 | 539 | var l = payload.length; |
|
534 | 540 | for (var i=0; i<l; i++) { |
|
535 | 541 | if (payload[i].source === 'IPython.zmq.page.page') { |
|
536 | 542 | if (payload[i].text.trim() !== '') { |
|
537 | 543 | IPython.pager.clear(); |
|
538 | 544 | IPython.pager.expand(); |
|
539 | 545 | IPython.pager.append_text(payload[i].text); |
|
540 | 546 | } |
|
541 | 547 | } else if (payload[i].source === 'IPython.zmq.zmqshell.ZMQInteractiveShell.set_next_input') { |
|
542 | 548 | var index = this.find_cell_index(cell); |
|
543 | 549 | var new_cell = this.insert_code_cell_after(index); |
|
544 | 550 | new_cell.set_code(payload[i].text); |
|
545 | 551 | this.dirty = true; |
|
546 | 552 | } |
|
547 | 553 | }; |
|
548 | 554 | }; |
|
549 | 555 | |
|
550 | 556 | |
|
551 | 557 | Notebook.prototype.handle_iopub_reply = function (e) { |
|
552 | 558 | reply = $.parseJSON(e.data); |
|
553 | 559 | var content = reply.content; |
|
554 | 560 | // console.log(reply); |
|
555 | 561 | var msg_type = reply.header.msg_type; |
|
556 | 562 | var cell = this.cell_for_msg(reply.parent_header.msg_id); |
|
557 | 563 | var output_types = ['stream','display_data','pyout','pyerr']; |
|
558 | 564 | if (output_types.indexOf(msg_type) >= 0) { |
|
559 | 565 | this.handle_output(cell, msg_type, content); |
|
560 | 566 | } else if (msg_type === 'status') { |
|
561 | 567 | if (content.execution_state === 'busy') { |
|
562 | 568 | IPython.kernel_status_widget.status_busy(); |
|
563 | 569 | } else if (content.execution_state === 'idle') { |
|
564 | 570 | IPython.kernel_status_widget.status_idle(); |
|
565 | 571 | } else if (content.execution_state === 'dead') { |
|
566 | 572 | this.handle_status_dead(); |
|
567 | 573 | }; |
|
568 | 574 | } |
|
569 | 575 | }; |
|
570 | 576 | |
|
571 | 577 | |
|
572 | 578 | Notebook.prototype.handle_status_dead = function () { |
|
573 | 579 | var that = this; |
|
574 | 580 | this.kernel.stop_channels(); |
|
575 | 581 | var dialog = $('<div/>'); |
|
576 | 582 | dialog.html('The kernel has died, would you like to restart it? If you do not restart the kernel, you will be able to save the notebook, but running code will not work until the notebook is reopened.'); |
|
577 | 583 | $(document).append(dialog); |
|
578 | 584 | dialog.dialog({ |
|
579 | 585 | resizable: false, |
|
580 | 586 | modal: true, |
|
581 | 587 | title: "Dead kernel", |
|
582 | 588 | buttons : { |
|
583 | 589 | "Yes": function () { |
|
584 | 590 | that.start_kernel(); |
|
585 | 591 | $(this).dialog('close'); |
|
586 | 592 | }, |
|
587 | 593 | "No": function () { |
|
588 | 594 | $(this).dialog('close'); |
|
589 | 595 | } |
|
590 | 596 | } |
|
591 | 597 | }); |
|
592 | 598 | }; |
|
593 | 599 | |
|
594 | 600 | |
|
595 | 601 | Notebook.prototype.handle_output = function (cell, msg_type, content) { |
|
596 | 602 | var json = {}; |
|
597 | 603 | json.output_type = msg_type; |
|
598 | 604 | if (msg_type === "stream") { |
|
599 | 605 | json.text = utils.fixConsole(content.data + '\n'); |
|
600 | 606 | } else if (msg_type === "display_data") { |
|
601 | 607 | json = this.convert_mime_types(json, content.data); |
|
602 | 608 | } else if (msg_type === "pyout") { |
|
603 | 609 | json.prompt_number = content.execution_count; |
|
604 | 610 | json = this.convert_mime_types(json, content.data); |
|
605 | 611 | } else if (msg_type === "pyerr") { |
|
606 | 612 | json.ename = content.ename; |
|
607 | 613 | json.evalue = content.evalue; |
|
608 | 614 | var traceback = []; |
|
609 | 615 | for (var i=0; i<content.traceback.length; i++) { |
|
610 | 616 | traceback.push(utils.fixConsole(content.traceback[i])); |
|
611 | 617 | } |
|
612 | 618 | json.traceback = traceback; |
|
613 | 619 | }; |
|
614 | 620 | cell.append_output(json); |
|
615 | 621 | this.dirty = true; |
|
616 | 622 | }; |
|
617 | 623 | |
|
618 | 624 | |
|
619 | 625 | Notebook.prototype.convert_mime_types = function (json, data) { |
|
620 | 626 | if (data['text/plain'] !== undefined) { |
|
621 | 627 | json.text = utils.fixConsole(data['text/plain']); |
|
622 | 628 | }; |
|
623 | 629 | if (data['text/html'] !== undefined) { |
|
624 | 630 | json.html = data['text/html']; |
|
625 | 631 | }; |
|
626 | 632 | if (data['image/svg+xml'] !== undefined) { |
|
627 | 633 | json.svg = data['image/svg+xml']; |
|
628 | 634 | }; |
|
629 | 635 | if (data['image/png'] !== undefined) { |
|
630 | 636 | json.png = data['image/png']; |
|
631 | 637 | }; |
|
632 | 638 | if (data['image/jpeg'] !== undefined) { |
|
633 | 639 | json.jpeg = data['image/jpeg']; |
|
634 | 640 | }; |
|
635 | 641 | if (data['text/latex'] !== undefined) { |
|
636 | 642 | json.latex = data['text/latex']; |
|
637 | 643 | }; |
|
638 | 644 | if (data['application/json'] !== undefined) { |
|
639 | 645 | json.json = data['application/json']; |
|
640 | 646 | }; |
|
641 | 647 | if (data['application/javascript'] !== undefined) { |
|
642 | 648 | json.javascript = data['application/javascript']; |
|
643 | 649 | } |
|
644 | 650 | return json; |
|
645 | 651 | }; |
|
646 | 652 | |
|
647 | 653 | |
|
648 | 654 | Notebook.prototype.execute_selected_cell = function (options) { |
|
649 | 655 | // add_new: should a new cell be added if we are at the end of the nb |
|
650 | 656 | // terminal: execute in terminal mode, which stays in the current cell |
|
651 | 657 | default_options = {terminal: false, add_new: true} |
|
652 | 658 | $.extend(default_options, options) |
|
653 | 659 | var that = this; |
|
654 | 660 | var cell = that.selected_cell(); |
|
655 | 661 | var cell_index = that.find_cell_index(cell); |
|
656 | 662 | if (cell instanceof IPython.CodeCell) { |
|
657 | 663 | cell.clear_output(); |
|
658 | 664 | var code = cell.get_code(); |
|
659 | 665 | var msg_id = that.kernel.execute(cell.get_code()); |
|
660 | 666 | that.msg_cell_map[msg_id] = cell.cell_id; |
|
661 | 667 | } else if (cell instanceof IPython.HTMLCell) { |
|
662 | 668 | cell.render(); |
|
663 | 669 | } |
|
664 | 670 | if (default_options.terminal) { |
|
665 | 671 | cell.clear_input(); |
|
666 | 672 | } else { |
|
667 | 673 | if ((cell_index === (that.ncells()-1)) && default_options.add_new) { |
|
668 | 674 | that.insert_code_cell_after(); |
|
669 | 675 | // If we are adding a new cell at the end, scroll down to show it. |
|
670 | 676 | that.scroll_to_bottom(); |
|
671 | 677 | } else { |
|
672 | 678 | that.select(cell_index+1); |
|
673 | 679 | }; |
|
674 | 680 | }; |
|
675 | 681 | this.dirty = true; |
|
676 | 682 | }; |
|
677 | 683 | |
|
678 | 684 | |
|
679 | 685 | Notebook.prototype.execute_all_cells = function () { |
|
680 | 686 | var ncells = this.ncells(); |
|
681 | 687 | for (var i=0; i<ncells; i++) { |
|
682 | 688 | this.select(i); |
|
683 | 689 | this.execute_selected_cell({add_new:false}); |
|
684 | 690 | }; |
|
685 | 691 | this.scroll_to_bottom(); |
|
686 | 692 | }; |
|
687 | 693 | |
|
688 | 694 | |
|
689 | 695 | Notebook.prototype.complete_cell = function (cell, line, cursor_pos) { |
|
690 | 696 | var msg_id = this.kernel.complete(line, cursor_pos); |
|
691 | 697 | this.msg_cell_map[msg_id] = cell.cell_id; |
|
692 | 698 | }; |
|
693 | 699 | |
|
694 | 700 | // Persistance and loading |
|
695 | 701 | |
|
696 | 702 | |
|
697 | 703 | Notebook.prototype.fromJSON = function (data) { |
|
698 | 704 | var ncells = this.ncells(); |
|
699 | 705 | for (var i=0; i<ncells; i++) { |
|
700 | 706 | // Always delete cell 0 as they get renumbered as they are deleted. |
|
701 | 707 | this.delete_cell(0); |
|
702 | 708 | }; |
|
703 | 709 | // Only handle 1 worksheet for now. |
|
704 | 710 | var worksheet = data.worksheets[0]; |
|
705 | 711 | if (worksheet !== undefined) { |
|
706 | 712 | var new_cells = worksheet.cells; |
|
707 | 713 | ncells = new_cells.length; |
|
708 | 714 | var cell_data = null; |
|
709 | 715 | var new_cell = null; |
|
710 | 716 | for (var i=0; i<ncells; i++) { |
|
711 | 717 | cell_data = new_cells[i]; |
|
712 | 718 | if (cell_data.cell_type == 'code') { |
|
713 | 719 | new_cell = this.insert_code_cell_after(); |
|
714 | 720 | new_cell.fromJSON(cell_data); |
|
715 | 721 | } else if (cell_data.cell_type === 'html') { |
|
716 | 722 | new_cell = this.insert_html_cell_after(); |
|
717 | 723 | new_cell.fromJSON(cell_data); |
|
718 | 724 | } else if (cell_data.cell_type === 'markdown') { |
|
719 | 725 | new_cell = this.insert_markdown_cell_after(); |
|
720 | 726 | new_cell.fromJSON(cell_data); |
|
721 | 727 | }; |
|
722 | 728 | }; |
|
723 | 729 | }; |
|
724 | 730 | }; |
|
725 | 731 | |
|
726 | 732 | |
|
727 | 733 | Notebook.prototype.toJSON = function () { |
|
728 | 734 | var cells = this.cells(); |
|
729 | 735 | var ncells = cells.length; |
|
730 | 736 | cell_array = new Array(ncells); |
|
731 | 737 | for (var i=0; i<ncells; i++) { |
|
732 | 738 | cell_array[i] = cells[i].toJSON(); |
|
733 | 739 | }; |
|
734 | 740 | data = { |
|
735 | 741 | // Only handle 1 worksheet for now. |
|
736 | 742 | worksheets : [{cells:cell_array}] |
|
737 | 743 | } |
|
738 | 744 | return data |
|
739 | 745 | }; |
|
740 | 746 | |
|
741 | 747 | Notebook.prototype.save_notebook = function () { |
|
742 | 748 | if (IPython.save_widget.test_notebook_name()) { |
|
743 | 749 | var notebook_id = IPython.save_widget.get_notebook_id(); |
|
744 | 750 | var nbname = IPython.save_widget.get_notebook_name(); |
|
745 | 751 | // We may want to move the name/id/nbformat logic inside toJSON? |
|
746 | 752 | var data = this.toJSON(); |
|
747 | 753 | data.name = nbname; |
|
748 | 754 | data.nbformat = 2; |
|
749 | 755 | // We do the call with settings so we can set cache to false. |
|
750 | 756 | var settings = { |
|
751 | 757 | processData : false, |
|
752 | 758 | cache : false, |
|
753 | 759 | type : "PUT", |
|
754 | 760 | data : JSON.stringify(data), |
|
755 | 761 | headers : {'Content-Type': 'application/json'}, |
|
756 | 762 | success : $.proxy(this.notebook_saved,this) |
|
757 | 763 | }; |
|
758 | 764 | IPython.save_widget.status_saving(); |
|
759 | 765 | $.ajax("/notebooks/" + notebook_id, settings); |
|
760 | 766 | }; |
|
761 | 767 | }; |
|
762 | 768 | |
|
763 | 769 | |
|
764 | 770 | Notebook.prototype.notebook_saved = function (data, status, xhr) { |
|
765 | 771 | this.dirty = false; |
|
766 | 772 | setTimeout($.proxy(IPython.save_widget.status_save,IPython.save_widget),500); |
|
767 | 773 | } |
|
768 | 774 | |
|
769 | 775 | |
|
770 | 776 | Notebook.prototype.load_notebook = function (callback) { |
|
771 | 777 | var that = this; |
|
772 | 778 | var notebook_id = IPython.save_widget.get_notebook_id(); |
|
773 | 779 | // We do the call with settings so we can set cache to false. |
|
774 | 780 | var settings = { |
|
775 | 781 | processData : false, |
|
776 | 782 | cache : false, |
|
777 | 783 | type : "GET", |
|
778 | 784 | dataType : "json", |
|
779 | 785 | success : function (data, status, xhr) { |
|
780 | 786 | that.notebook_loaded(data, status, xhr); |
|
781 | 787 | if (callback !== undefined) { |
|
782 | 788 | callback(); |
|
783 | 789 | }; |
|
784 | 790 | } |
|
785 | 791 | }; |
|
786 | 792 | IPython.save_widget.status_loading(); |
|
787 | 793 | $.ajax("/notebooks/" + notebook_id, settings); |
|
788 | 794 | } |
|
789 | 795 | |
|
790 | 796 | |
|
791 | 797 | Notebook.prototype.notebook_loaded = function (data, status, xhr) { |
|
792 | 798 | this.fromJSON(data); |
|
793 | 799 | if (this.ncells() === 0) { |
|
794 | 800 | this.insert_code_cell_after(); |
|
795 | 801 | }; |
|
796 | 802 | IPython.save_widget.status_save(); |
|
797 | 803 | IPython.save_widget.set_notebook_name(data.name); |
|
798 | 804 | this.start_kernel(); |
|
799 | 805 | this.dirty = false; |
|
800 | 806 | // fromJSON always selects the last cell inserted. We need to wait |
|
801 | 807 | // until that is done before scrolling to the top. |
|
802 | 808 | setTimeout(function () { |
|
803 | 809 | IPython.notebook.select(0); |
|
804 | 810 | IPython.notebook.scroll_to_top(); |
|
805 | 811 | }, 50); |
|
806 | 812 | }; |
|
807 | 813 | |
|
808 | 814 | IPython.Notebook = Notebook; |
|
809 | 815 | |
|
810 | 816 | return IPython; |
|
811 | 817 | |
|
812 | 818 | }(IPython)); |
|
813 | 819 |
@@ -1,51 +1,57 b'' | |||
|
1 | //---------------------------------------------------------------------------- | |
|
2 | // Copyright (C) 2008-2011 The IPython Development Team | |
|
3 | // | |
|
4 | // Distributed under the terms of the BSD License. The full license is in | |
|
5 | // the file COPYING, distributed as part of this software. | |
|
6 | //---------------------------------------------------------------------------- | |
|
1 | 7 |
|
|
2 | 8 | //============================================================================ |
|
3 | 9 | // On document ready |
|
4 | 10 | //============================================================================ |
|
5 | 11 | |
|
6 | 12 | |
|
7 | 13 | $(document).ready(function () { |
|
8 | 14 | |
|
9 | 15 | MathJax.Hub.Config({ |
|
10 | 16 | tex2jax: { |
|
11 | 17 | inlineMath: [ ['$','$'], ["\\(","\\)"] ], |
|
12 | 18 | displayMath: [ ['$$','$$'], ["\\[","\\]"] ], |
|
13 | 19 | }, |
|
14 | 20 | displayAlign: 'left', // Change this to 'center' to center equations. |
|
15 | 21 | "HTML-CSS": { |
|
16 | 22 | styles: {'.MathJax_Display': {"margin": 0}} |
|
17 | 23 | } |
|
18 | 24 | }); |
|
19 | 25 | IPython.markdown_converter = new Markdown.Converter(); |
|
20 | 26 | |
|
21 | 27 | $('div#header').addClass('border-box-sizing'); |
|
22 | 28 | $('div#main_app').addClass('border-box-sizing ui-widget ui-widget-content'); |
|
23 | 29 | $('div#notebook_panel').addClass('border-box-sizing ui-widget'); |
|
24 | 30 | |
|
25 | 31 | IPython.layout_manager = new IPython.LayoutManager(); |
|
26 | 32 | IPython.pager = new IPython.Pager('div#pager', 'div#pager_splitter'); |
|
27 | 33 | IPython.left_panel = new IPython.LeftPanel('div#left_panel', 'div#left_panel_splitter'); |
|
28 | 34 | IPython.save_widget = new IPython.SaveWidget('span#save_widget'); |
|
29 | 35 | IPython.notebook = new IPython.Notebook('div#notebook'); |
|
30 | 36 | IPython.kernel_status_widget = new IPython.KernelStatusWidget('#kernel_status'); |
|
31 | 37 | IPython.kernel_status_widget.status_idle(); |
|
32 | 38 | |
|
33 | 39 | IPython.layout_manager.do_resize(); |
|
34 | 40 | |
|
35 | 41 | // These have display: none in the css file and are made visible here to prevent FLOUC. |
|
36 | 42 | $('div#header').css('display','block'); |
|
37 | 43 | $('div#main_app').css('display','block'); |
|
38 | 44 | |
|
39 | 45 | // Perform these actions after the notebook has been loaded. |
|
40 | 46 | // We wait 100 milliseconds because the notebook scrolls to the top after a load |
|
41 | 47 | // is completed and we need to wait for that to mostly finish. |
|
42 | 48 | IPython.notebook.load_notebook(function () { |
|
43 | 49 | setTimeout(function () { |
|
44 | 50 | IPython.save_widget.update_url(); |
|
45 | 51 | IPython.layout_manager.do_resize(); |
|
46 | 52 | IPython.pager.collapse(); |
|
47 | 53 | },100); |
|
48 | 54 | }); |
|
49 | 55 | |
|
50 | 56 | }); |
|
51 | 57 |
@@ -1,238 +1,244 b'' | |||
|
1 | //---------------------------------------------------------------------------- | |
|
2 | // Copyright (C) 2008-2011 The IPython Development Team | |
|
3 | // | |
|
4 | // Distributed under the terms of the BSD License. The full license is in | |
|
5 | // the file COPYING, distributed as part of this software. | |
|
6 | //---------------------------------------------------------------------------- | |
|
1 | 7 |
|
|
2 | 8 | //============================================================================ |
|
3 | // Cell | |
|
9 | // NotebookList | |
|
4 | 10 | //============================================================================ |
|
5 | 11 | |
|
6 | 12 | var IPython = (function (IPython) { |
|
7 | 13 | |
|
8 | 14 | var NotebookList = function (selector) { |
|
9 | 15 | this.selector = selector; |
|
10 | 16 | if (this.selector !== undefined) { |
|
11 | 17 | this.element = $(selector); |
|
12 | 18 | this.style(); |
|
13 | 19 | this.bind_events(); |
|
14 | 20 | } |
|
15 | 21 | }; |
|
16 | 22 | |
|
17 | 23 | NotebookList.prototype.style = function () { |
|
18 | 24 | this.element.addClass('ui-widget ui-widget-content'); |
|
19 | 25 | $('div#project_name').addClass('ui-widget ui-widget-header'); |
|
20 | 26 | }; |
|
21 | 27 | |
|
22 | 28 | |
|
23 | 29 | NotebookList.prototype.bind_events = function () { |
|
24 | 30 | var that = this; |
|
25 | 31 | this.element.bind('dragover', function () { |
|
26 | 32 | return false; |
|
27 | 33 | }); |
|
28 | 34 | this.element.bind('drop', function (event) { |
|
29 | 35 | var files = event.originalEvent.dataTransfer.files; |
|
30 | 36 | for (var i = 0, f; f = files[i]; i++) { |
|
31 | 37 | var reader = new FileReader(); |
|
32 | 38 | reader.readAsText(f); |
|
33 | 39 | var fname = f.name.split('.'); |
|
34 | 40 | var nbname = fname[0]; |
|
35 | 41 | var nbformat = fname[1]; |
|
36 | 42 | if (nbformat === 'ipynb') {nbformat = 'xml';}; |
|
37 | 43 | if (nbformat === 'xml' || nbformat === 'py' || nbformat === 'json') { |
|
38 | 44 | var item = that.new_notebook_item(0); |
|
39 | 45 | that.add_name_input(nbname, item); |
|
40 | 46 | item.data('nbformat', nbformat); |
|
41 | 47 | // Store the notebook item in the reader so we can use it later |
|
42 | 48 | // to know which item it belongs to. |
|
43 | 49 | $(reader).data('item', item); |
|
44 | 50 | reader.onload = function (event) { |
|
45 | 51 | var nbitem = $(event.target).data('item'); |
|
46 | 52 | that.add_notebook_data(event.target.result, nbitem); |
|
47 | 53 | that.add_upload_button(nbitem); |
|
48 | 54 | }; |
|
49 | 55 | }; |
|
50 | 56 | } |
|
51 | 57 | return false; |
|
52 | 58 | }); |
|
53 | 59 | }; |
|
54 | 60 | |
|
55 | 61 | |
|
56 | 62 | NotebookList.prototype.load_list = function () { |
|
57 | 63 | var settings = { |
|
58 | 64 | processData : false, |
|
59 | 65 | cache : false, |
|
60 | 66 | type : "GET", |
|
61 | 67 | dataType : "json", |
|
62 | 68 | success : $.proxy(this.list_loaded, this) |
|
63 | 69 | }; |
|
64 | 70 | $.ajax("/notebooks", settings); |
|
65 | 71 | }; |
|
66 | 72 | |
|
67 | 73 | |
|
68 | 74 | NotebookList.prototype.list_loaded = function (data, status, xhr) { |
|
69 | 75 | var len = data.length; |
|
70 | 76 | // Todo: remove old children |
|
71 | 77 | for (var i=0; i<len; i++) { |
|
72 | 78 | var notebook_id = data[i].notebook_id; |
|
73 | 79 | var nbname = data[i].name; |
|
74 | 80 | var item = this.new_notebook_item(i); |
|
75 | 81 | this.add_link(notebook_id, nbname, item); |
|
76 | 82 | this.add_delete_button(item); |
|
77 | 83 | }; |
|
78 | 84 | }; |
|
79 | 85 | |
|
80 | 86 | |
|
81 | 87 | NotebookList.prototype.new_notebook_item = function (index) { |
|
82 | 88 | var item = $('<div/>'); |
|
83 | 89 | item.addClass('notebook_item ui-widget ui-widget-content ui-helper-clearfix'); |
|
84 | 90 | var item_name = $('<span/>').addClass('item_name'); |
|
85 | 91 | |
|
86 | 92 | item.append(item_name); |
|
87 | 93 | if (index === -1) { |
|
88 | 94 | this.element.append(item); |
|
89 | 95 | } else { |
|
90 | 96 | this.element.children().eq(index).after(item); |
|
91 | 97 | } |
|
92 | 98 | return item; |
|
93 | 99 | }; |
|
94 | 100 | |
|
95 | 101 | |
|
96 | 102 | NotebookList.prototype.add_link = function (notebook_id, nbname, item) { |
|
97 | 103 | item.data('nbname', nbname); |
|
98 | 104 | item.data('notebook_id', notebook_id); |
|
99 | 105 | var new_item_name = $('<span/>').addClass('item_name'); |
|
100 | 106 | new_item_name.append( |
|
101 | 107 | $('<a/>'). |
|
102 | 108 | attr('href','/'+notebook_id). |
|
103 | 109 | attr('target','_blank'). |
|
104 | 110 | text(nbname) |
|
105 | 111 | ); |
|
106 | 112 | var e = item.find('.item_name'); |
|
107 | 113 | if (e.length === 0) { |
|
108 | 114 | item.append(new_item_name); |
|
109 | 115 | } else { |
|
110 | 116 | e.replaceWith(new_item_name); |
|
111 | 117 | }; |
|
112 | 118 | }; |
|
113 | 119 | |
|
114 | 120 | |
|
115 | 121 | NotebookList.prototype.add_name_input = function (nbname, item) { |
|
116 | 122 | item.data('nbname', nbname); |
|
117 | 123 | var new_item_name = $('<span/>').addClass('item_name'); |
|
118 | 124 | new_item_name.append( |
|
119 | 125 | $('<input/>').addClass('ui-widget ui-widget-content'). |
|
120 | 126 | attr('value', nbname). |
|
121 | 127 | attr('size', '30'). |
|
122 | 128 | attr('type', 'text') |
|
123 | 129 | ); |
|
124 | 130 | var e = item.find('.item_name'); |
|
125 | 131 | if (e.length === 0) { |
|
126 | 132 | item.append(new_item_name); |
|
127 | 133 | } else { |
|
128 | 134 | e.replaceWith(new_item_name); |
|
129 | 135 | }; |
|
130 | 136 | }; |
|
131 | 137 | |
|
132 | 138 | |
|
133 | 139 | NotebookList.prototype.add_notebook_data = function (data, item) { |
|
134 | 140 | item.data('nbdata',data); |
|
135 | 141 | }; |
|
136 | 142 | |
|
137 | 143 | |
|
138 | 144 | NotebookList.prototype.add_delete_button = function (item) { |
|
139 | 145 | var new_buttons = $('<span/>').addClass('item_buttons'); |
|
140 | 146 | var delete_button = $('<button>Delete</button>').button(). |
|
141 | 147 | click(function (e) { |
|
142 | 148 | // $(this) is the button that was clicked. |
|
143 | 149 | var that = $(this); |
|
144 | 150 | // We use the nbname and notebook_id from the parent notebook_item element's |
|
145 | 151 | // data because the outer scopes values change as we iterate through the loop. |
|
146 | 152 | var parent_item = that.parents('div.notebook_item'); |
|
147 | 153 | var nbname = parent_item.data('nbname'); |
|
148 | 154 | var notebook_id = parent_item.data('notebook_id'); |
|
149 | 155 | var dialog = $('<div/>'); |
|
150 | 156 | dialog.html('Are you sure you want to permanently delete the notebook: ' + nbname + '?'); |
|
151 | 157 | parent_item.append(dialog); |
|
152 | 158 | dialog.dialog({ |
|
153 | 159 | resizable: false, |
|
154 | 160 | modal: true, |
|
155 | 161 | title: "Delete notebook", |
|
156 | 162 | buttons : { |
|
157 | 163 | "Delete": function () { |
|
158 | 164 | var settings = { |
|
159 | 165 | processData : false, |
|
160 | 166 | cache : false, |
|
161 | 167 | type : "DELETE", |
|
162 | 168 | dataType : "json", |
|
163 | 169 | success : function (data, status, xhr) { |
|
164 | 170 | parent_item.remove(); |
|
165 | 171 | } |
|
166 | 172 | }; |
|
167 | 173 | $.ajax("/notebooks/" + notebook_id, settings); |
|
168 | 174 | $(this).dialog('close'); |
|
169 | 175 | }, |
|
170 | 176 | "Cancel": function () { |
|
171 | 177 | $(this).dialog('close'); |
|
172 | 178 | } |
|
173 | 179 | } |
|
174 | 180 | }); |
|
175 | 181 | }); |
|
176 | 182 | new_buttons.append(delete_button); |
|
177 | 183 | var e = item.find('.item_buttons'); |
|
178 | 184 | if (e.length === 0) { |
|
179 | 185 | item.append(new_buttons); |
|
180 | 186 | } else { |
|
181 | 187 | e.replaceWith(new_buttons); |
|
182 | 188 | }; |
|
183 | 189 | }; |
|
184 | 190 | |
|
185 | 191 | |
|
186 | 192 | NotebookList.prototype.add_upload_button = function (item) { |
|
187 | 193 | var that = this; |
|
188 | 194 | var new_buttons = $('<span/>').addClass('item_buttons'); |
|
189 | 195 | var upload_button = $('<button>Upload</button>').button(). |
|
190 | 196 | click(function (e) { |
|
191 | 197 | var nbname = item.find('.item_name > input').attr('value'); |
|
192 | 198 | var nbformat = item.data('nbformat'); |
|
193 | 199 | var nbdata = item.data('nbdata'); |
|
194 | 200 | var content_type = 'text/plain'; |
|
195 | 201 | if (nbformat === 'xml') { |
|
196 | 202 | content_type = 'application/xml'; |
|
197 | 203 | } else if (nbformat === 'json') { |
|
198 | 204 | content_type = 'application/json'; |
|
199 | 205 | } else if (nbformat === 'py') { |
|
200 | 206 | content_type = 'application/x-python'; |
|
201 | 207 | }; |
|
202 | 208 | var settings = { |
|
203 | 209 | processData : false, |
|
204 | 210 | cache : false, |
|
205 | 211 | type : 'POST', |
|
206 | 212 | dataType : 'json', |
|
207 | 213 | data : nbdata, |
|
208 | 214 | headers : {'Content-Type': content_type}, |
|
209 | 215 | success : function (data, status, xhr) { |
|
210 | 216 | that.add_link(data, nbname, item); |
|
211 | 217 | that.add_delete_button(item); |
|
212 | 218 | } |
|
213 | 219 | }; |
|
214 | 220 | |
|
215 | 221 | var qs = $.param({name:nbname, format:nbformat}); |
|
216 | 222 | $.ajax('/notebooks?' + qs, settings); |
|
217 | 223 | }); |
|
218 | 224 | var cancel_button = $('<button>Cancel</button>').button(). |
|
219 | 225 | click(function (e) { |
|
220 | 226 | item.remove(); |
|
221 | 227 | }); |
|
222 | 228 | upload_button.addClass('upload_button'); |
|
223 | 229 | new_buttons.append(upload_button).append(cancel_button); |
|
224 | 230 | var e = item.find('.item_buttons'); |
|
225 | 231 | if (e.length === 0) { |
|
226 | 232 | item.append(new_buttons); |
|
227 | 233 | } else { |
|
228 | 234 | e.replaceWith(new_buttons); |
|
229 | 235 | }; |
|
230 | 236 | }; |
|
231 | 237 | |
|
232 | 238 | |
|
233 | 239 | IPython.NotebookList = NotebookList; |
|
234 | 240 | |
|
235 | 241 | return IPython; |
|
236 | 242 | |
|
237 | 243 | }(IPython)); |
|
238 | 244 |
@@ -1,95 +1,101 b'' | |||
|
1 | //---------------------------------------------------------------------------- | |
|
2 | // Copyright (C) 2008-2011 The IPython Development Team | |
|
3 | // | |
|
4 | // Distributed under the terms of the BSD License. The full license is in | |
|
5 | // the file COPYING, distributed as part of this software. | |
|
6 | //---------------------------------------------------------------------------- | |
|
1 | 7 |
|
|
2 | 8 | //============================================================================ |
|
3 | 9 | // Pager |
|
4 | 10 | //============================================================================ |
|
5 | 11 | |
|
6 | 12 | var IPython = (function (IPython) { |
|
7 | 13 | |
|
8 | 14 | var utils = IPython.utils; |
|
9 | 15 | |
|
10 | 16 | var Pager = function (pager_selector, pager_splitter_selector) { |
|
11 | 17 | this.pager_element = $(pager_selector); |
|
12 | 18 | this.pager_splitter_element = $(pager_splitter_selector); |
|
13 | 19 | this.expanded = true; |
|
14 | 20 | this.percentage_height = 0.40; |
|
15 | 21 | this.style(); |
|
16 | 22 | this.bind_events(); |
|
17 | 23 | }; |
|
18 | 24 | |
|
19 | 25 | |
|
20 | 26 | Pager.prototype.style = function () { |
|
21 | 27 | this.pager_splitter_element.addClass('border-box-sizing ui-widget ui-state-default'); |
|
22 | 28 | this.pager_element.addClass('border-box-sizing ui-widget'); |
|
23 | 29 | }; |
|
24 | 30 | |
|
25 | 31 | |
|
26 | 32 | Pager.prototype.bind_events = function () { |
|
27 | 33 | var that = this; |
|
28 | 34 | |
|
29 | 35 | this.pager_element.bind('collapse_pager', function () { |
|
30 | 36 | that.pager_element.hide('fast'); |
|
31 | 37 | }); |
|
32 | 38 | |
|
33 | 39 | this.pager_element.bind('expand_pager', function () { |
|
34 | 40 | that.pager_element.show('fast'); |
|
35 | 41 | }); |
|
36 | 42 | |
|
37 | 43 | this.pager_splitter_element.hover( |
|
38 | 44 | function () { |
|
39 | 45 | that.pager_splitter_element.addClass('ui-state-hover'); |
|
40 | 46 | }, |
|
41 | 47 | function () { |
|
42 | 48 | that.pager_splitter_element.removeClass('ui-state-hover'); |
|
43 | 49 | } |
|
44 | 50 | ); |
|
45 | 51 | |
|
46 | 52 | this.pager_splitter_element.click(function () { |
|
47 | 53 | that.toggle(); |
|
48 | 54 | }); |
|
49 | 55 | |
|
50 | 56 | }; |
|
51 | 57 | |
|
52 | 58 | |
|
53 | 59 | Pager.prototype.collapse = function () { |
|
54 | 60 | if (this.expanded === true) { |
|
55 | 61 | this.pager_element.add($('div#notebook')).trigger('collapse_pager'); |
|
56 | 62 | this.expanded = false; |
|
57 | 63 | }; |
|
58 | 64 | }; |
|
59 | 65 | |
|
60 | 66 | |
|
61 | 67 | Pager.prototype.expand = function () { |
|
62 | 68 | if (this.expanded !== true) { |
|
63 | 69 | this.pager_element.add($('div#notebook')).trigger('expand_pager'); |
|
64 | 70 | this.expanded = true; |
|
65 | 71 | }; |
|
66 | 72 | }; |
|
67 | 73 | |
|
68 | 74 | |
|
69 | 75 | Pager.prototype.toggle = function () { |
|
70 | 76 | if (this.expanded === true) { |
|
71 | 77 | this.collapse(); |
|
72 | 78 | } else { |
|
73 | 79 | this.expand(); |
|
74 | 80 | }; |
|
75 | 81 | }; |
|
76 | 82 | |
|
77 | 83 | |
|
78 | 84 | Pager.prototype.clear = function (text) { |
|
79 | 85 | this.pager_element.empty(); |
|
80 | 86 | }; |
|
81 | 87 | |
|
82 | 88 | |
|
83 | 89 | Pager.prototype.append_text = function (text) { |
|
84 | 90 | var toinsert = $("<div/>").addClass("output_area output_stream"); |
|
85 | 91 | toinsert.append($('<pre/>').html(utils.fixConsole(text))); |
|
86 | 92 | this.pager_element.append(toinsert); |
|
87 | 93 | }; |
|
88 | 94 | |
|
89 | 95 | |
|
90 | 96 | IPython.Pager = Pager; |
|
91 | 97 | |
|
92 | 98 | return IPython; |
|
93 | 99 | |
|
94 | 100 | }(IPython)); |
|
95 | 101 |
@@ -1,245 +1,251 b'' | |||
|
1 | //---------------------------------------------------------------------------- | |
|
2 | // Copyright (C) 2008-2011 The IPython Development Team | |
|
3 | // | |
|
4 | // Distributed under the terms of the BSD License. The full license is in | |
|
5 | // the file COPYING, distributed as part of this software. | |
|
6 | //---------------------------------------------------------------------------- | |
|
1 | 7 |
|
|
2 | 8 | //============================================================================ |
|
3 | // Cell | |
|
9 | // PanelSection | |
|
4 | 10 | //============================================================================ |
|
5 | 11 | |
|
6 | 12 | var IPython = (function (IPython) { |
|
7 | 13 | |
|
8 | 14 | var utils = IPython.utils; |
|
9 | 15 | |
|
10 | 16 | // Base PanelSection class |
|
11 | 17 | |
|
12 | 18 | var PanelSection = function (selector) { |
|
13 | 19 | this.selector = selector; |
|
14 | 20 | if (this.selector !== undefined) { |
|
15 | 21 | this.element = $(selector); |
|
16 | 22 | this.header = this.element.find('h3.section_header'); |
|
17 | 23 | this.content = this.element.find('div.section_content'); |
|
18 | 24 | this.style(); |
|
19 | 25 | this.bind_events(); |
|
20 | 26 | } |
|
21 | 27 | this.expanded = true; |
|
22 | 28 | }; |
|
23 | 29 | |
|
24 | 30 | |
|
25 | 31 | PanelSection.prototype.style = function () { |
|
26 | 32 | this.header.addClass('ui-widget ui-state-default'); |
|
27 | 33 | this.content.addClass('ui-widget section_content'); |
|
28 | 34 | }; |
|
29 | 35 | |
|
30 | 36 | |
|
31 | 37 | PanelSection.prototype.bind_events = function () { |
|
32 | 38 | var that = this; |
|
33 | 39 | this.header.click(function () { |
|
34 | 40 | that.toggle(); |
|
35 | 41 | }); |
|
36 | 42 | this.header.hover(function () { |
|
37 | 43 | that.header.toggleClass('ui-state-hover'); |
|
38 | 44 | }); |
|
39 | 45 | }; |
|
40 | 46 | |
|
41 | 47 | |
|
42 | 48 | PanelSection.prototype.expand = function () { |
|
43 | 49 | if (!this.expanded) { |
|
44 | 50 | this.content.slideDown('fast'); |
|
45 | 51 | this.expanded = true; |
|
46 | 52 | }; |
|
47 | 53 | }; |
|
48 | 54 | |
|
49 | 55 | |
|
50 | 56 | PanelSection.prototype.collapse = function () { |
|
51 | 57 | if (this.expanded) { |
|
52 | 58 | this.content.slideUp('fast'); |
|
53 | 59 | this.expanded = false; |
|
54 | 60 | }; |
|
55 | 61 | }; |
|
56 | 62 | |
|
57 | 63 | |
|
58 | 64 | PanelSection.prototype.toggle = function () { |
|
59 | 65 | if (this.expanded === true) { |
|
60 | 66 | this.collapse(); |
|
61 | 67 | } else { |
|
62 | 68 | this.expand(); |
|
63 | 69 | }; |
|
64 | 70 | }; |
|
65 | 71 | |
|
66 | 72 | |
|
67 | 73 | PanelSection.prototype.create_children = function () {}; |
|
68 | 74 | |
|
69 | 75 | |
|
70 | 76 | // NotebookSection |
|
71 | 77 | |
|
72 | 78 | var NotebookSection = function () { |
|
73 | 79 | PanelSection.apply(this, arguments); |
|
74 | 80 | }; |
|
75 | 81 | |
|
76 | 82 | |
|
77 | 83 | NotebookSection.prototype = new PanelSection(); |
|
78 | 84 | |
|
79 | 85 | |
|
80 | 86 | NotebookSection.prototype.style = function () { |
|
81 | 87 | PanelSection.prototype.style.apply(this); |
|
82 | 88 | this.content.addClass('ui-helper-clearfix'); |
|
83 | 89 | this.content.find('div.section_row').addClass('ui-helper-clearfix'); |
|
84 | 90 | this.content.find('#new_open').buttonset(); |
|
85 | 91 | this.content.find('#download_notebook').button(); |
|
86 | 92 | this.content.find('#upload_notebook').button(); |
|
87 | 93 | this.content.find('#download_format').addClass('ui-widget ui-widget-content'); |
|
88 | 94 | this.content.find('#download_format option').addClass('ui-widget ui-widget-content'); |
|
89 | 95 | }; |
|
90 | 96 | |
|
91 | 97 | |
|
92 | 98 | NotebookSection.prototype.bind_events = function () { |
|
93 | 99 | PanelSection.prototype.bind_events.apply(this); |
|
94 | 100 | var that = this; |
|
95 | 101 | this.content.find('#new_notebook').click(function () { |
|
96 | 102 | window.open('/new'); |
|
97 | 103 | }); |
|
98 | 104 | this.content.find('#open_notebook').click(function () { |
|
99 | 105 | window.open('/'); |
|
100 | 106 | }); |
|
101 | 107 | this.content.find('#download_notebook').click(function () { |
|
102 | 108 | var format = that.content.find('#download_format').val(); |
|
103 | 109 | var notebook_id = IPython.save_widget.get_notebook_id(); |
|
104 | 110 | var url = '/notebooks/' + notebook_id + '?format=' + format; |
|
105 | 111 | window.open(url,'_newtab'); |
|
106 | 112 | }); |
|
107 | 113 | }; |
|
108 | 114 | |
|
109 | 115 | // CellSection |
|
110 | 116 | |
|
111 | 117 | var CellSection = function () { |
|
112 | 118 | PanelSection.apply(this, arguments); |
|
113 | 119 | }; |
|
114 | 120 | |
|
115 | 121 | |
|
116 | 122 | CellSection.prototype = new PanelSection(); |
|
117 | 123 | |
|
118 | 124 | |
|
119 | 125 | CellSection.prototype.style = function () { |
|
120 | 126 | PanelSection.prototype.style.apply(this); |
|
121 | 127 | this.content.addClass('ui-helper-clearfix'); |
|
122 | 128 | this.content.find('div.section_row').addClass('ui-helper-clearfix'); |
|
123 | 129 | this.content.find('#delete_cell').button(); |
|
124 | 130 | this.content.find('#insert').buttonset(); |
|
125 | 131 | this.content.find('#move').buttonset(); |
|
126 | 132 | this.content.find('#cell_type').buttonset(); |
|
127 | 133 | this.content.find('#toggle_output').buttonset(); |
|
128 | 134 | this.content.find('#run_cells').buttonset(); |
|
129 | 135 | }; |
|
130 | 136 | |
|
131 | 137 | |
|
132 | 138 | CellSection.prototype.bind_events = function () { |
|
133 | 139 | PanelSection.prototype.bind_events.apply(this); |
|
134 | 140 | this.content.find('#collapse_cell').click(function () { |
|
135 | 141 | IPython.notebook.collapse(); |
|
136 | 142 | }); |
|
137 | 143 | this.content.find('#expand_cell').click(function () { |
|
138 | 144 | IPython.notebook.expand(); |
|
139 | 145 | }); |
|
140 | 146 | this.content.find('#clear_all_output').click(function () { |
|
141 | 147 | IPython.notebook.clear_all_output(); |
|
142 | 148 | }); |
|
143 | 149 | this.content.find('#delete_cell').click(function () { |
|
144 | 150 | IPython.notebook.delete_cell(); |
|
145 | 151 | }); |
|
146 | 152 | this.content.find('#insert_cell_above').click(function () { |
|
147 | 153 | IPython.notebook.insert_code_cell_before(); |
|
148 | 154 | }); |
|
149 | 155 | this.content.find('#insert_cell_below').click(function () { |
|
150 | 156 | IPython.notebook.insert_code_cell_after(); |
|
151 | 157 | }); |
|
152 | 158 | this.content.find('#move_cell_up').click(function () { |
|
153 | 159 | IPython.notebook.move_cell_up(); |
|
154 | 160 | }); |
|
155 | 161 | this.content.find('#move_cell_down').click(function () { |
|
156 | 162 | IPython.notebook.move_cell_down(); |
|
157 | 163 | }); |
|
158 | 164 | this.content.find('#to_code').click(function () { |
|
159 | 165 | IPython.notebook.to_code(); |
|
160 | 166 | }); |
|
161 | 167 | this.content.find('#to_html').click(function () { |
|
162 | 168 | IPython.notebook.to_html(); |
|
163 | 169 | }); |
|
164 | 170 | this.content.find('#to_markdown').click(function () { |
|
165 | 171 | IPython.notebook.to_markdown(); |
|
166 | 172 | }); |
|
167 | 173 | this.content.find('#run_selected_cell').click(function () { |
|
168 | 174 | IPython.notebook.execute_selected_cell(); |
|
169 | 175 | }); |
|
170 | 176 | this.content.find('#run_all_cells').click(function () { |
|
171 | 177 | IPython.notebook.execute_all_cells(); |
|
172 | 178 | }); |
|
173 | 179 | this.content.find('#autoindent').change(function () { |
|
174 | 180 | var state = $('#autoindent').prop('checked'); |
|
175 | 181 | IPython.notebook.set_autoindent(state); |
|
176 | 182 | }); |
|
177 | 183 | }; |
|
178 | 184 | |
|
179 | 185 | |
|
180 | 186 | // KernelSection |
|
181 | 187 | |
|
182 | 188 | var KernelSection = function () { |
|
183 | 189 | PanelSection.apply(this, arguments); |
|
184 | 190 | }; |
|
185 | 191 | |
|
186 | 192 | |
|
187 | 193 | KernelSection.prototype = new PanelSection(); |
|
188 | 194 | |
|
189 | 195 | |
|
190 | 196 | KernelSection.prototype.style = function () { |
|
191 | 197 | PanelSection.prototype.style.apply(this); |
|
192 | 198 | this.content.addClass('ui-helper-clearfix'); |
|
193 | 199 | this.content.find('div.section_row').addClass('ui-helper-clearfix'); |
|
194 | 200 | this.content.find('#int_restart').buttonset(); |
|
195 | 201 | }; |
|
196 | 202 | |
|
197 | 203 | |
|
198 | 204 | KernelSection.prototype.bind_events = function () { |
|
199 | 205 | PanelSection.prototype.bind_events.apply(this); |
|
200 | 206 | this.content.find('#restart_kernel').click(function () { |
|
201 | 207 | IPython.notebook.restart_kernel(); |
|
202 | 208 | }); |
|
203 | 209 | this.content.find('#int_kernel').click(function () { |
|
204 | 210 | IPython.notebook.kernel.interrupt(); |
|
205 | 211 | }); |
|
206 | 212 | }; |
|
207 | 213 | |
|
208 | 214 | |
|
209 | 215 | // HelpSection |
|
210 | 216 | |
|
211 | 217 | var HelpSection = function () { |
|
212 | 218 | PanelSection.apply(this, arguments); |
|
213 | 219 | }; |
|
214 | 220 | |
|
215 | 221 | |
|
216 | 222 | HelpSection.prototype = new PanelSection(); |
|
217 | 223 | |
|
218 | 224 | |
|
219 | 225 | HelpSection.prototype.style = function () { |
|
220 | 226 | PanelSection.prototype.style.apply(this); |
|
221 | 227 | PanelSection.prototype.style.apply(this); |
|
222 | 228 | this.content.addClass('ui-helper-clearfix'); |
|
223 | 229 | this.content.find('div.section_row').addClass('ui-helper-clearfix'); |
|
224 | 230 | this.content.find('#help_buttons0').buttonset(); |
|
225 | 231 | this.content.find('#help_buttons1').buttonset(); |
|
226 | 232 | }; |
|
227 | 233 | |
|
228 | 234 | |
|
229 | 235 | HelpSection.prototype.bind_events = function () { |
|
230 | 236 | PanelSection.prototype.bind_events.apply(this); |
|
231 | 237 | }; |
|
232 | 238 | |
|
233 | 239 | |
|
234 | 240 | // Set module variables |
|
235 | 241 | |
|
236 | 242 | IPython.PanelSection = PanelSection; |
|
237 | 243 | IPython.NotebookSection = NotebookSection; |
|
238 | 244 | IPython.CellSection = CellSection; |
|
239 | 245 | IPython.KernelSection = KernelSection; |
|
240 | 246 | IPython.HelpSection = HelpSection; |
|
241 | 247 | |
|
242 | 248 | return IPython; |
|
243 | 249 | |
|
244 | 250 | }(IPython)); |
|
245 | 251 |
@@ -1,109 +1,115 b'' | |||
|
1 | //---------------------------------------------------------------------------- | |
|
2 | // Copyright (C) 2008-2011 The IPython Development Team | |
|
3 | // | |
|
4 | // Distributed under the terms of the BSD License. The full license is in | |
|
5 | // the file COPYING, distributed as part of this software. | |
|
6 | //---------------------------------------------------------------------------- | |
|
1 | 7 |
|
|
2 | 8 | //============================================================================ |
|
3 | // Cell | |
|
9 | // SaveWidget | |
|
4 | 10 | //============================================================================ |
|
5 | 11 | |
|
6 | 12 | var IPython = (function (IPython) { |
|
7 | 13 | |
|
8 | 14 | var utils = IPython.utils; |
|
9 | 15 | |
|
10 | 16 | var SaveWidget = function (selector) { |
|
11 | 17 | this.selector = selector; |
|
12 | 18 | this.notebook_name_re = /[^/\\]+/ |
|
13 | 19 | if (this.selector !== undefined) { |
|
14 | 20 | this.element = $(selector); |
|
15 | 21 | this.style(); |
|
16 | 22 | this.bind_events(); |
|
17 | 23 | } |
|
18 | 24 | }; |
|
19 | 25 | |
|
20 | 26 | |
|
21 | 27 | SaveWidget.prototype.style = function () { |
|
22 | 28 | this.element.find('input#notebook_name').addClass('ui-widget ui-widget-content'); |
|
23 | 29 | this.element.find('button#save_notebook').button(); |
|
24 | 30 | var left_panel_width = $('div#left_panel').outerWidth(); |
|
25 | 31 | var left_panel_splitter_width = $('div#left_panel_splitter').outerWidth(); |
|
26 | 32 | $('span#save_widget').css({marginLeft:left_panel_width+left_panel_splitter_width}); |
|
27 | 33 | }; |
|
28 | 34 | |
|
29 | 35 | |
|
30 | 36 | SaveWidget.prototype.bind_events = function () { |
|
31 | 37 | var that = this; |
|
32 | 38 | this.element.find('button#save_notebook').click(function () { |
|
33 | 39 | IPython.notebook.save_notebook(); |
|
34 | 40 | that.set_document_title(); |
|
35 | 41 | }); |
|
36 | 42 | }; |
|
37 | 43 | |
|
38 | 44 | |
|
39 | 45 | SaveWidget.prototype.get_notebook_name = function () { |
|
40 | 46 | return this.element.find('input#notebook_name').attr('value'); |
|
41 | 47 | } |
|
42 | 48 | |
|
43 | 49 | |
|
44 | 50 | SaveWidget.prototype.set_notebook_name = function (nbname) { |
|
45 | 51 | this.element.find('input#notebook_name').attr('value',nbname); |
|
46 | 52 | this.set_document_title(); |
|
47 | 53 | } |
|
48 | 54 | |
|
49 | 55 | |
|
50 | 56 | SaveWidget.prototype.set_document_title = function () { |
|
51 | 57 | nbname = this.get_notebook_name(); |
|
52 | 58 | document.title = 'IPy: ' + nbname; |
|
53 | 59 | }; |
|
54 | 60 | |
|
55 | 61 | |
|
56 | 62 | SaveWidget.prototype.get_notebook_id = function () { |
|
57 | 63 | return this.element.find('span#notebook_id').text() |
|
58 | 64 | }; |
|
59 | 65 | |
|
60 | 66 | |
|
61 | 67 | SaveWidget.prototype.update_url = function () { |
|
62 | 68 | var notebook_id = this.get_notebook_id(); |
|
63 | 69 | if (notebook_id !== '') { |
|
64 | 70 | window.history.replaceState({}, '', notebook_id); |
|
65 | 71 | }; |
|
66 | 72 | }; |
|
67 | 73 | |
|
68 | 74 | |
|
69 | 75 | SaveWidget.prototype.test_notebook_name = function () { |
|
70 | 76 | var nbname = this.get_notebook_name(); |
|
71 | 77 | if (this.notebook_name_re.test(nbname)) { |
|
72 | 78 | return true; |
|
73 | 79 | } else { |
|
74 | 80 | var bad_name = $('<div/>'); |
|
75 | 81 | bad_name.html( |
|
76 | 82 | "The notebook name you entered (" + |
|
77 | 83 | nbname + |
|
78 | 84 | ") is not valid. Notebook names can contain any characters except / and \\" |
|
79 | 85 | ); |
|
80 | 86 | bad_name.dialog({title: 'Invalid name', modal: true}); |
|
81 | 87 | return false; |
|
82 | 88 | }; |
|
83 | 89 | }; |
|
84 | 90 | |
|
85 | 91 | |
|
86 | 92 | SaveWidget.prototype.status_save = function () { |
|
87 | 93 | this.element.find('span.ui-button-text').text('Save'); |
|
88 | 94 | this.element.find('button#save_notebook').button('enable'); |
|
89 | 95 | }; |
|
90 | 96 | |
|
91 | 97 | |
|
92 | 98 | SaveWidget.prototype.status_saving = function () { |
|
93 | 99 | this.element.find('span.ui-button-text').text('Saving'); |
|
94 | 100 | this.element.find('button#save_notebook').button('disable'); |
|
95 | 101 | }; |
|
96 | 102 | |
|
97 | 103 | |
|
98 | 104 | SaveWidget.prototype.status_loading = function () { |
|
99 | 105 | this.element.find('span.ui-button-text').text('Loading'); |
|
100 | 106 | this.element.find('button#save_notebook').button('disable'); |
|
101 | 107 | }; |
|
102 | 108 | |
|
103 | 109 | |
|
104 | 110 | IPython.SaveWidget = SaveWidget; |
|
105 | 111 | |
|
106 | 112 | return IPython; |
|
107 | 113 | |
|
108 | 114 | }(IPython)); |
|
109 | 115 |
@@ -1,248 +1,254 b'' | |||
|
1 | //---------------------------------------------------------------------------- | |
|
2 | // Copyright (C) 2008-2011 The IPython Development Team | |
|
3 | // | |
|
4 | // Distributed under the terms of the BSD License. The full license is in | |
|
5 | // the file COPYING, distributed as part of this software. | |
|
6 | //---------------------------------------------------------------------------- | |
|
1 | 7 |
|
|
2 | 8 | //============================================================================ |
|
3 | 9 | // TextCell |
|
4 | 10 | //============================================================================ |
|
5 | 11 | |
|
6 | 12 | var IPython = (function (IPython) { |
|
7 | 13 | |
|
8 | 14 | // TextCell base class |
|
9 | 15 | |
|
10 | 16 | var TextCell = function (notebook) { |
|
11 | 17 | this.code_mirror_mode = this.code_mirror_mode || 'htmlmixed'; |
|
12 | 18 | this.placeholder = this.placeholder || ''; |
|
13 | 19 | IPython.Cell.apply(this, arguments); |
|
14 | 20 | this.rendered = false; |
|
15 | 21 | this.cell_type = this.cell_type || 'text'; |
|
16 | 22 | }; |
|
17 | 23 | |
|
18 | 24 | |
|
19 | 25 | TextCell.prototype = new IPython.Cell(); |
|
20 | 26 | |
|
21 | 27 | |
|
22 | 28 | TextCell.prototype.create_element = function () { |
|
23 | 29 | var cell = $("<div>").addClass('cell text_cell border-box-sizing'); |
|
24 | 30 | var input_area = $('<div/>').addClass('text_cell_input'); |
|
25 | 31 | this.code_mirror = CodeMirror(input_area.get(0), { |
|
26 | 32 | indentUnit : 4, |
|
27 | 33 | mode: this.code_mirror_mode, |
|
28 | 34 | theme: 'default', |
|
29 | 35 | value: this.placeholder |
|
30 | 36 | }); |
|
31 | 37 | // The tabindex=-1 makes this div focusable. |
|
32 | 38 | var render_area = $('<div/>').addClass('text_cell_render'). |
|
33 | 39 | addClass('rendered_html').attr('tabindex','-1'); |
|
34 | 40 | cell.append(input_area).append(render_area); |
|
35 | 41 | this.element = cell; |
|
36 | 42 | }; |
|
37 | 43 | |
|
38 | 44 | |
|
39 | 45 | TextCell.prototype.bind_events = function () { |
|
40 | 46 | IPython.Cell.prototype.bind_events.apply(this); |
|
41 | 47 | var that = this; |
|
42 | 48 | this.element.keydown(function (event) { |
|
43 | 49 | if (event.which === 13) { |
|
44 | 50 | if (that.rendered) { |
|
45 | 51 | that.edit(); |
|
46 | 52 | event.preventDefault(); |
|
47 | 53 | }; |
|
48 | 54 | }; |
|
49 | 55 | }); |
|
50 | 56 | }; |
|
51 | 57 | |
|
52 | 58 | |
|
53 | 59 | TextCell.prototype.select = function () { |
|
54 | 60 | IPython.Cell.prototype.select.apply(this); |
|
55 | 61 | var output = this.element.find("div.text_cell_render"); |
|
56 | 62 | output.trigger('focus'); |
|
57 | 63 | }; |
|
58 | 64 | |
|
59 | 65 | |
|
60 | 66 | TextCell.prototype.edit = function () { |
|
61 | 67 | if (this.rendered === true) { |
|
62 | 68 | var text_cell = this.element; |
|
63 | 69 | var output = text_cell.find("div.text_cell_render"); |
|
64 | 70 | output.hide(); |
|
65 | 71 | text_cell.find('div.text_cell_input').show(); |
|
66 | 72 | this.code_mirror.focus(); |
|
67 | 73 | this.code_mirror.refresh(); |
|
68 | 74 | this.rendered = false; |
|
69 | 75 | }; |
|
70 | 76 | }; |
|
71 | 77 | |
|
72 | 78 | |
|
73 | 79 | // Subclasses must define render. |
|
74 | 80 | TextCell.prototype.render = function () {}; |
|
75 | 81 | |
|
76 | 82 | |
|
77 | 83 | TextCell.prototype.config_mathjax = function () { |
|
78 | 84 | var text_cell = this.element; |
|
79 | 85 | var that = this; |
|
80 | 86 | text_cell.click(function () { |
|
81 | 87 | that.edit(); |
|
82 | 88 | }).focusout(function () { |
|
83 | 89 | that.render(); |
|
84 | 90 | }); |
|
85 | 91 | |
|
86 | 92 | text_cell.trigger("focusout"); |
|
87 | 93 | }; |
|
88 | 94 | |
|
89 | 95 | |
|
90 | 96 | TextCell.prototype.get_source = function() { |
|
91 | 97 | return this.code_mirror.getValue(); |
|
92 | 98 | }; |
|
93 | 99 | |
|
94 | 100 | |
|
95 | 101 | TextCell.prototype.set_source = function(text) { |
|
96 | 102 | this.code_mirror.setValue(text); |
|
97 | 103 | this.code_mirror.refresh(); |
|
98 | 104 | }; |
|
99 | 105 | |
|
100 | 106 | |
|
101 | 107 | TextCell.prototype.get_rendered = function() { |
|
102 | 108 | return this.element.find('div.text_cell_render').html(); |
|
103 | 109 | }; |
|
104 | 110 | |
|
105 | 111 | |
|
106 | 112 | TextCell.prototype.set_rendered = function(text) { |
|
107 | 113 | this.element.find('div.text_cell_render').html(text); |
|
108 | 114 | }; |
|
109 | 115 | |
|
110 | 116 | |
|
111 | 117 | TextCell.prototype.at_top = function () { |
|
112 | 118 | if (this.rendered) { |
|
113 | 119 | return true; |
|
114 | 120 | } else { |
|
115 | 121 | return false; |
|
116 | 122 | } |
|
117 | 123 | }; |
|
118 | 124 | |
|
119 | 125 | |
|
120 | 126 | TextCell.prototype.at_bottom = function () { |
|
121 | 127 | if (this.rendered) { |
|
122 | 128 | return true; |
|
123 | 129 | } else { |
|
124 | 130 | return false; |
|
125 | 131 | } |
|
126 | 132 | }; |
|
127 | 133 | |
|
128 | 134 | |
|
129 | 135 | TextCell.prototype.fromJSON = function (data) { |
|
130 | 136 | if (data.cell_type === this.cell_type) { |
|
131 | 137 | if (data.source !== undefined) { |
|
132 | 138 | this.set_source(data.source); |
|
133 | 139 | this.set_rendered(data.rendered || ''); |
|
134 | 140 | this.rendered = false; |
|
135 | 141 | this.render(); |
|
136 | 142 | }; |
|
137 | 143 | }; |
|
138 | 144 | }; |
|
139 | 145 | |
|
140 | 146 | |
|
141 | 147 | TextCell.prototype.toJSON = function () { |
|
142 | 148 | var data = {} |
|
143 | 149 | data.cell_type = this.cell_type; |
|
144 | 150 | data.source = this.get_source(); |
|
145 | 151 | return data; |
|
146 | 152 | }; |
|
147 | 153 | |
|
148 | 154 | |
|
149 | 155 | // HTMLCell |
|
150 | 156 | |
|
151 | 157 | var HTMLCell = function (notebook) { |
|
152 | 158 | this.placeholder = "Type <strong>HTML</strong> and LaTeX: $\\alpha^2$"; |
|
153 | 159 | IPython.TextCell.apply(this, arguments); |
|
154 | 160 | this.cell_type = 'html'; |
|
155 | 161 | }; |
|
156 | 162 | |
|
157 | 163 | |
|
158 | 164 | HTMLCell.prototype = new TextCell(); |
|
159 | 165 | |
|
160 | 166 | |
|
161 | 167 | HTMLCell.prototype.render = function () { |
|
162 | 168 | if (this.rendered === false) { |
|
163 | 169 | var text = this.get_source(); |
|
164 | 170 | if (text === "") {text = this.placeholder;}; |
|
165 | 171 | this.set_rendered(text); |
|
166 | 172 | MathJax.Hub.Queue(["Typeset",MathJax.Hub]); |
|
167 | 173 | this.element.find('div.text_cell_input').hide(); |
|
168 | 174 | this.element.find("div.text_cell_render").show(); |
|
169 | 175 | this.rendered = true; |
|
170 | 176 | }; |
|
171 | 177 | }; |
|
172 | 178 | |
|
173 | 179 | |
|
174 | 180 | // MarkdownCell |
|
175 | 181 | |
|
176 | 182 | var MarkdownCell = function (notebook) { |
|
177 | 183 | this.placeholder = "Type *Markdown* and LaTeX: $\\alpha^2$"; |
|
178 | 184 | IPython.TextCell.apply(this, arguments); |
|
179 | 185 | this.cell_type = 'markdown'; |
|
180 | 186 | }; |
|
181 | 187 | |
|
182 | 188 | |
|
183 | 189 | MarkdownCell.prototype = new TextCell(); |
|
184 | 190 | |
|
185 | 191 | |
|
186 | 192 | MarkdownCell.prototype.render = function () { |
|
187 | 193 | if (this.rendered === false) { |
|
188 | 194 | var text = this.get_source(); |
|
189 | 195 | if (text === "") {text = this.placeholder;}; |
|
190 | 196 | var html = IPython.markdown_converter.makeHtml(text); |
|
191 | 197 | this.set_rendered(html); |
|
192 | 198 | MathJax.Hub.Queue(["Typeset",MathJax.Hub]); |
|
193 | 199 | this.element.find('div.text_cell_input').hide(); |
|
194 | 200 | this.element.find("div.text_cell_render").show(); |
|
195 | 201 | this.rendered = true; |
|
196 | 202 | }; |
|
197 | 203 | }; |
|
198 | 204 | |
|
199 | 205 | |
|
200 | 206 | // RSTCell |
|
201 | 207 | |
|
202 | 208 | var RSTCell = function (notebook) { |
|
203 | 209 | this.placeholder = "Type *ReStructured Text* and LaTeX: $\\alpha^2$"; |
|
204 | 210 | IPython.TextCell.apply(this, arguments); |
|
205 | 211 | this.cell_type = 'rst'; |
|
206 | 212 | }; |
|
207 | 213 | |
|
208 | 214 | |
|
209 | 215 | RSTCell.prototype = new TextCell(); |
|
210 | 216 | |
|
211 | 217 | |
|
212 | 218 | RSTCell.prototype.render = function () { |
|
213 | 219 | if (this.rendered === false) { |
|
214 | 220 | var text = this.get_source(); |
|
215 | 221 | if (text === "") {text = this.placeholder;}; |
|
216 | 222 | var settings = { |
|
217 | 223 | processData : false, |
|
218 | 224 | cache : false, |
|
219 | 225 | type : "POST", |
|
220 | 226 | data : text, |
|
221 | 227 | headers : {'Content-Type': 'application/x-rst'}, |
|
222 | 228 | success : $.proxy(this.handle_render,this) |
|
223 | 229 | }; |
|
224 | 230 | $.ajax("/rstservice/render", settings); |
|
225 | 231 | this.element.find('div.text_cell_input').hide(); |
|
226 | 232 | this.element.find("div.text_cell_render").show(); |
|
227 | 233 | this.set_rendered("Rendering..."); |
|
228 | 234 | }; |
|
229 | 235 | }; |
|
230 | 236 | |
|
231 | 237 | |
|
232 | 238 | RSTCell.prototype.handle_render = function (data, status, xhr) { |
|
233 | 239 | this.set_rendered(data); |
|
234 | 240 | MathJax.Hub.Queue(["Typeset",MathJax.Hub]); |
|
235 | 241 | this.rendered = true; |
|
236 | 242 | }; |
|
237 | 243 | |
|
238 | 244 | |
|
239 | 245 | IPython.TextCell = TextCell; |
|
240 | 246 | IPython.HTMLCell = HTMLCell; |
|
241 | 247 | IPython.MarkdownCell = MarkdownCell; |
|
242 | 248 | IPython.RSTCell = RSTCell; |
|
243 | 249 | |
|
244 | 250 | |
|
245 | 251 | return IPython; |
|
246 | 252 | |
|
247 | 253 | }(IPython)); |
|
248 | 254 |
@@ -1,95 +1,101 b'' | |||
|
1 | //---------------------------------------------------------------------------- | |
|
2 | // Copyright (C) 2008-2011 The IPython Development Team | |
|
3 | // | |
|
4 | // Distributed under the terms of the BSD License. The full license is in | |
|
5 | // the file COPYING, distributed as part of this software. | |
|
6 | //---------------------------------------------------------------------------- | |
|
1 | 7 |
|
|
2 | 8 | //============================================================================ |
|
3 | 9 | // Utilities |
|
4 | 10 | //============================================================================ |
|
5 | 11 | |
|
6 | 12 | IPython.namespace('IPython.utils') |
|
7 | 13 | |
|
8 | 14 | IPython.utils = (function (IPython) { |
|
9 | 15 | |
|
10 | 16 | var uuid = function () { |
|
11 | 17 | // http://www.ietf.org/rfc/rfc4122.txt |
|
12 | 18 | var s = []; |
|
13 | 19 | var hexDigits = "0123456789ABCDEF"; |
|
14 | 20 | for (var i = 0; i < 32; i++) { |
|
15 | 21 | s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1); |
|
16 | 22 | } |
|
17 | 23 | s[12] = "4"; // bits 12-15 of the time_hi_and_version field to 0010 |
|
18 | 24 | s[16] = hexDigits.substr((s[16] & 0x3) | 0x8, 1); // bits 6-7 of the clock_seq_hi_and_reserved to 01 |
|
19 | 25 | |
|
20 | 26 | var uuid = s.join(""); |
|
21 | 27 | return uuid; |
|
22 | 28 | }; |
|
23 | 29 | |
|
24 | 30 | |
|
25 | 31 | //Fix raw text to parse correctly in crazy XML |
|
26 | 32 | function xmlencode(string) { |
|
27 | 33 | return string.replace(/\&/g,'&'+'amp;') |
|
28 | 34 | .replace(/</g,'&'+'lt;') |
|
29 | 35 | .replace(/>/g,'&'+'gt;') |
|
30 | 36 | .replace(/\'/g,'&'+'apos;') |
|
31 | 37 | .replace(/\"/g,'&'+'quot;') |
|
32 | 38 | .replace(/`/g,'&'+'#96;') |
|
33 | 39 | } |
|
34 | 40 | |
|
35 | 41 | |
|
36 | 42 | //Map from terminal commands to CSS classes |
|
37 | 43 | ansi_colormap = { |
|
38 | 44 | "30":"ansiblack", "31":"ansired", |
|
39 | 45 | "32":"ansigreen", "33":"ansiyellow", |
|
40 | 46 | "34":"ansiblue", "35":"ansipurple","36":"ansicyan", |
|
41 | 47 | "37":"ansigrey", "01":"ansibold" |
|
42 | 48 | } |
|
43 | 49 | |
|
44 | 50 | // Transform ANI color escape codes into HTML <span> tags with css |
|
45 | 51 | // classes listed in the above ansi_colormap object. The actual color used |
|
46 | 52 | // are set in the css file. |
|
47 | 53 | function fixConsole(txt) { |
|
48 | 54 | txt = xmlencode(txt) |
|
49 | 55 | var re = /\033\[([\d;]*?)m/ |
|
50 | 56 | var opened = false |
|
51 | 57 | var cmds = [] |
|
52 | 58 | var opener = "" |
|
53 | 59 | var closer = "" |
|
54 | 60 | |
|
55 | 61 | while (re.test(txt)) { |
|
56 | 62 | var cmds = txt.match(re)[1].split(";") |
|
57 | 63 | closer = opened?"</span>":"" |
|
58 | 64 | opened = cmds.length > 1 || cmds[0] != 0 |
|
59 | 65 | var rep = [] |
|
60 | 66 | for (var i in cmds) |
|
61 | 67 | if (typeof(ansi_colormap[cmds[i]]) != "undefined") |
|
62 | 68 | rep.push(ansi_colormap[cmds[i]]) |
|
63 | 69 | opener = rep.length > 0?"<span class=\""+rep.join(" ")+"\">":"" |
|
64 | 70 | txt = txt.replace(re, closer + opener) |
|
65 | 71 | } |
|
66 | 72 | if (opened) txt += "</span>" |
|
67 | 73 | return txt.trim() |
|
68 | 74 | } |
|
69 | 75 | |
|
70 | 76 | |
|
71 | 77 | grow = function(element) { |
|
72 | 78 | // Grow the cell by hand. This is used upon reloading from JSON, when the |
|
73 | 79 | // autogrow handler is not called. |
|
74 | 80 | var dom = element.get(0); |
|
75 | 81 | var lines_count = 0; |
|
76 | 82 | // modified split rule from |
|
77 | 83 | // http://stackoverflow.com/questions/2035910/how-to-get-the-number-of-lines-in-a-textarea/2036424#2036424 |
|
78 | 84 | var lines = dom.value.split(/\r|\r\n|\n/); |
|
79 | 85 | lines_count = lines.length; |
|
80 | 86 | if (lines_count >= 1) { |
|
81 | 87 | dom.rows = lines_count; |
|
82 | 88 | } else { |
|
83 | 89 | dom.rows = 1; |
|
84 | 90 | } |
|
85 | 91 | }; |
|
86 | 92 | |
|
87 | 93 | |
|
88 | 94 | return { |
|
89 | 95 | uuid : uuid, |
|
90 | 96 | fixConsole : fixConsole, |
|
91 | 97 | grow : grow |
|
92 | 98 | } |
|
93 | 99 | |
|
94 | 100 | }(IPython)); |
|
95 | 101 |
@@ -1,39 +1,26 b'' | |||
|
1 | 1 | """Tests for the notebook kernel and session manager.""" |
|
2 | 2 | |
|
3 | 3 | from unittest import TestCase |
|
4 | 4 | |
|
5 | 5 | from IPython.frontend.html.notebook.kernelmanager import KernelManager |
|
6 | from IPython.frontend.html.notebook.sessionmanager import SessionManagerRunningError | |
|
7 | 6 | |
|
8 | 7 | class TestKernelManager(TestCase): |
|
9 | 8 | |
|
10 | 9 | def test_km_lifecycle(self): |
|
11 | 10 | km = KernelManager() |
|
12 | 11 | kid = km.start_kernel() |
|
13 | 12 | self.assert_(kid in km) |
|
14 | 13 | self.assertEquals(len(km),1) |
|
15 | 14 | km.kill_kernel(kid) |
|
16 | 15 | self.assert_(not kid in km) |
|
17 | 16 | |
|
18 | 17 | kid = km.start_kernel() |
|
19 | 18 | self.assertEquals('127.0.0.1',km.get_kernel_ip(kid)) |
|
20 | 19 | port_dict = km.get_kernel_ports(kid) |
|
21 | 20 | self.assert_('stdin_port' in port_dict) |
|
22 | 21 | self.assert_('iopub_port' in port_dict) |
|
23 | 22 | self.assert_('shell_port' in port_dict) |
|
24 | 23 | self.assert_('hb_port' in port_dict) |
|
25 | 24 | km.get_kernel_process(kid) |
|
26 | 25 | |
|
27 | def test_session_manager(self): | |
|
28 | km = KernelManager() | |
|
29 | kid = km.start_kernel() | |
|
30 | sm = km.create_session_manager(kid) | |
|
31 | self.assert_(sm._running) | |
|
32 | sm.stop() | |
|
33 | self.assert_(not sm._running) | |
|
34 | sm.start() | |
|
35 | self.assertRaises(SessionManagerRunningError, sm.start) | |
|
36 | sm.get_iopub_stream() | |
|
37 | sm.get_shell_stream() | |
|
38 | sm.session | |
|
39 | 26 |
@@ -1,80 +1,96 b'' | |||
|
1 | import json | |
|
1 | """Unfinished code for ZMQ/HTTP bridging. We use WebSockets instead. | |
|
2 | 2 |
|
|
3 | from tornado import web | |
|
3 | Authors: | |
|
4 | ||
|
5 | * Brian Granger | |
|
6 | """ | |
|
4 | 7 | |
|
8 | #----------------------------------------------------------------------------- | |
|
9 | # Copyright (C) 2008-2011 The IPython Development Team | |
|
10 | # | |
|
11 | # Distributed under the terms of the BSD License. The full license is in | |
|
12 | # the file COPYING, distributed as part of this software. | |
|
13 | #----------------------------------------------------------------------------- | |
|
14 | ||
|
15 | #----------------------------------------------------------------------------- | |
|
16 | # Imports | |
|
17 | #----------------------------------------------------------------------------- | |
|
18 | ||
|
19 | import json | |
|
5 | 20 | import logging |
|
6 | 21 | |
|
22 | from tornado import web | |
|
23 | ||
|
24 | #----------------------------------------------------------------------------- | |
|
25 | # Code | |
|
26 | #----------------------------------------------------------------------------- | |
|
7 | 27 | |
|
8 | 28 | class ZMQHandler(web.RequestHandler): |
|
9 | 29 | |
|
10 | 30 | def get_stream(self): |
|
11 | 31 | """Get the ZMQStream for this request.""" |
|
12 | 32 | raise NotImplementedError('Implement get_stream() in a subclass.') |
|
13 | 33 | |
|
14 | 34 | def _save_method_args(self, *args, **kwargs): |
|
15 | 35 | """Save the args and kwargs to get/post/put/delete for future use. |
|
16 | 36 | |
|
17 | 37 | These arguments are not saved in the request or handler objects, but |
|
18 | 38 | are often needed by methods such as get_stream(). |
|
19 | 39 | """ |
|
20 | 40 | self._method_args = args |
|
21 | 41 | self._method_kwargs = kwargs |
|
22 | 42 | |
|
23 | 43 | def _handle_msgs(self, msg): |
|
24 | 44 | msgs = [msg] |
|
25 | 45 | stream = self.get_stream() |
|
26 | 46 | stream.on_recv(lambda m: msgs.append(json.loads(m))) |
|
27 | 47 | stream.flush() |
|
28 | 48 | stream.stop_on_recv() |
|
29 | 49 | logging.info("Reply: %r" % msgs) |
|
30 | 50 | self.write(json.dumps(msgs)) |
|
31 | 51 | self.finish() |
|
32 | 52 | |
|
33 | 53 | |
|
34 | 54 | class ZMQPubHandler(ZMQHandler): |
|
35 | 55 | |
|
36 | 56 | SUPPORTED_METHODS = ("POST",) |
|
37 | 57 | |
|
38 | 58 | def post(self, *args, **kwargs): |
|
39 | 59 | self._save_method_args(*args, **kwargs) |
|
40 | 60 | try: |
|
41 | 61 | msg = json.loads(self.request.body) |
|
42 | 62 | except: |
|
43 | 63 | self.send_error(status_code=415) |
|
44 | 64 | else: |
|
45 | 65 | logging.info("Request: %r" % msg) |
|
46 | 66 | self.get_stream().send_json(msg) |
|
47 | 67 | |
|
48 | 68 | |
|
49 | 69 | class ZMQSubHandler(ZMQHandler): |
|
50 | 70 | |
|
51 | 71 | SUPPORTED_METHODS = ("GET",) |
|
52 | 72 | |
|
53 | 73 | @web.asynchronous |
|
54 | 74 | def get(self, *args, **kwargs): |
|
55 | 75 | self._save_method_args(*args, **kwargs) |
|
56 | 76 | self.get_stream().on_recv(self._handle_msgs) |
|
57 | 77 | |
|
58 | 78 | |
|
59 | 79 | class ZMQXReqHandler(ZMQHandler): |
|
60 | 80 | |
|
61 | 81 | SUPPORTED_METHODS = ("POST",) |
|
62 | 82 | |
|
63 | 83 | @web.asynchronous |
|
64 | 84 | def post(self, *args, **kwargs): |
|
65 | 85 | self._save_method_args(*args, **kwargs) |
|
66 | 86 | logging.info("request: %r" % self.request) |
|
67 | 87 | try: |
|
68 | 88 | msg = json.loads(self.request.body) |
|
69 | 89 | except: |
|
70 | 90 | self.send_error(status_code=415) |
|
71 | 91 | else: |
|
72 | 92 | logging.info("Reply: %r" % msg) |
|
73 | 93 | stream = self.get_stream() |
|
74 | 94 | stream.send_json(msg) |
|
75 | 95 | stream.on_recv(self._handle_msgs) |
|
76 | 96 | |
|
77 | ||
|
78 | ||
|
79 | ||
|
80 | No newline at end of file |
@@ -1,196 +1,217 b'' | |||
|
1 | """The official API for working with notebooks in the current format version. | |
|
2 | ||
|
3 | Authors: | |
|
4 | ||
|
5 | * Brian Granger | |
|
6 | """ | |
|
7 | ||
|
8 | #----------------------------------------------------------------------------- | |
|
9 | # Copyright (C) 2008-2011 The IPython Development Team | |
|
10 | # | |
|
11 | # Distributed under the terms of the BSD License. The full license is in | |
|
12 | # the file COPYING, distributed as part of this software. | |
|
13 | #----------------------------------------------------------------------------- | |
|
14 | ||
|
15 | #----------------------------------------------------------------------------- | |
|
16 | # Imports | |
|
17 | #----------------------------------------------------------------------------- | |
|
18 | ||
|
1 | 19 | import json |
|
2 | 20 | from xml.etree import ElementTree as ET |
|
3 | 21 | import re |
|
4 | 22 | |
|
5 | 23 | from IPython.nbformat import v2 |
|
6 | 24 | from IPython.nbformat import v1 |
|
7 | 25 | |
|
8 | 26 | from IPython.nbformat.v2 import ( |
|
9 | 27 | NotebookNode, |
|
10 | 28 | new_code_cell, new_text_cell, new_notebook, new_output, new_worksheet, |
|
11 | 29 | parse_filename |
|
12 | 30 | ) |
|
13 | 31 | |
|
32 | #----------------------------------------------------------------------------- | |
|
33 | # Code | |
|
34 | #----------------------------------------------------------------------------- | |
|
14 | 35 | |
|
15 | 36 | current_nbformat = 2 |
|
16 | 37 | |
|
17 | 38 | |
|
18 | 39 | class NBFormatError(Exception): |
|
19 | 40 | pass |
|
20 | 41 | |
|
21 | 42 | |
|
22 | 43 | def parse_json(s, **kwargs): |
|
23 | 44 | """Parse a string into a (nbformat, dict) tuple.""" |
|
24 | 45 | d = json.loads(s, **kwargs) |
|
25 | 46 | nbformat = d.get('nbformat',1) |
|
26 | 47 | return nbformat, d |
|
27 | 48 | |
|
28 | 49 | |
|
29 | 50 | def parse_xml(s, **kwargs): |
|
30 | 51 | """Parse a string into a (nbformat, etree) tuple.""" |
|
31 | 52 | root = ET.fromstring(s) |
|
32 | 53 | nbformat_e = root.find('nbformat') |
|
33 | 54 | if nbformat_e is not None: |
|
34 | 55 | nbformat = int(nbformat_e.text) |
|
35 | 56 | else: |
|
36 | 57 | raise NBFormatError('No nbformat version found') |
|
37 | 58 | return nbformat, root |
|
38 | 59 | |
|
39 | 60 | |
|
40 | 61 | def parse_py(s, **kwargs): |
|
41 | 62 | """Parse a string into a (nbformat, string) tuple.""" |
|
42 | 63 | pattern = r'# <nbformat>(?P<nbformat>\d+)</nbformat>' |
|
43 | 64 | m = re.search(pattern,s) |
|
44 | 65 | if m is not None: |
|
45 | 66 | nbformat = int(m.group('nbformat')) |
|
46 | 67 | else: |
|
47 | 68 | nbformat = 2 |
|
48 | 69 | return nbformat, s |
|
49 | 70 | |
|
50 | 71 | |
|
51 | 72 | def reads_json(s, **kwargs): |
|
52 | 73 | """Read a JSON notebook from a string and return the NotebookNode object.""" |
|
53 | 74 | nbformat, d = parse_json(s, **kwargs) |
|
54 | 75 | if nbformat == 1: |
|
55 | 76 | nb = v1.to_notebook_json(d, **kwargs) |
|
56 | 77 | nb = v2.convert_to_this_nbformat(nb, orig_version=1) |
|
57 | 78 | elif nbformat == 2: |
|
58 | 79 | nb = v2.to_notebook_json(d, **kwargs) |
|
59 | 80 | else: |
|
60 | 81 | raise NBFormatError('Unsupported JSON nbformat version: %i' % nbformat) |
|
61 | 82 | return nb |
|
62 | 83 | |
|
63 | 84 | |
|
64 | 85 | def writes_json(nb, **kwargs): |
|
65 | 86 | return v2.writes_json(nb, **kwargs) |
|
66 | 87 | |
|
67 | 88 | |
|
68 | 89 | def reads_xml(s, **kwargs): |
|
69 | 90 | """Read an XML notebook from a string and return the NotebookNode object.""" |
|
70 | 91 | nbformat, root = parse_xml(s, **kwargs) |
|
71 | 92 | if nbformat == 2: |
|
72 | 93 | nb = v2.to_notebook_xml(root, **kwargs) |
|
73 | 94 | else: |
|
74 | 95 | raise NBFormatError('Unsupported XML nbformat version: %i' % nbformat) |
|
75 | 96 | return nb |
|
76 | 97 | |
|
77 | 98 | |
|
78 | 99 | def writes_xml(nb, **kwargs): |
|
79 | 100 | return v2.writes_xml(nb, **kwargs) |
|
80 | 101 | |
|
81 | 102 | |
|
82 | 103 | def reads_py(s, **kwargs): |
|
83 | 104 | """Read a .py notebook from a string and return the NotebookNode object.""" |
|
84 | 105 | nbformat, s = parse_py(s, **kwargs) |
|
85 | 106 | if nbformat == 2: |
|
86 | 107 | nb = v2.to_notebook_py(s, **kwargs) |
|
87 | 108 | else: |
|
88 | 109 | raise NBFormatError('Unsupported PY nbformat version: %i' % nbformat) |
|
89 | 110 | return nb |
|
90 | 111 | |
|
91 | 112 | |
|
92 | 113 | def writes_py(nb, **kwargs): |
|
93 | 114 | return v2.writes_py(nb, **kwargs) |
|
94 | 115 | |
|
95 | 116 | |
|
96 | 117 | # High level API |
|
97 | 118 | |
|
98 | 119 | |
|
99 | 120 | def reads(s, format, **kwargs): |
|
100 | 121 | """Read a notebook from a string and return the NotebookNode object. |
|
101 | 122 | |
|
102 | 123 | This function properly handles notebooks of any version. The notebook |
|
103 | 124 | returned will always be in the current version's format. |
|
104 | 125 | |
|
105 | 126 | Parameters |
|
106 | 127 | ---------- |
|
107 | 128 | s : str |
|
108 | 129 | The raw string to read the notebook from. |
|
109 | 130 | format : ('xml','json','py') |
|
110 | 131 | The format that the string is in. |
|
111 | 132 | |
|
112 | 133 | Returns |
|
113 | 134 | ------- |
|
114 | 135 | nb : NotebookNode |
|
115 | 136 | The notebook that was read. |
|
116 | 137 | """ |
|
117 | 138 | if format == 'xml': |
|
118 | 139 | return reads_xml(s, **kwargs) |
|
119 | 140 | elif format == 'json': |
|
120 | 141 | return reads_json(s, **kwargs) |
|
121 | 142 | elif format == 'py': |
|
122 | 143 | return reads_py(s, **kwargs) |
|
123 | 144 | else: |
|
124 | 145 | raise NBFormatError('Unsupported format: %s' % format) |
|
125 | 146 | |
|
126 | 147 | |
|
127 | 148 | def writes(nb, format, **kwargs): |
|
128 | 149 | """Write a notebook to a string in a given format in the current nbformat version. |
|
129 | 150 | |
|
130 | 151 | This function always writes the notebook in the current nbformat version. |
|
131 | 152 | |
|
132 | 153 | Parameters |
|
133 | 154 | ---------- |
|
134 | 155 | nb : NotebookNode |
|
135 | 156 | The notebook to write. |
|
136 | 157 | format : ('xml','json','py') |
|
137 | 158 | The format to write the notebook in. |
|
138 | 159 | |
|
139 | 160 | Returns |
|
140 | 161 | ------- |
|
141 | 162 | s : str |
|
142 | 163 | The notebook string. |
|
143 | 164 | """ |
|
144 | 165 | if format == 'xml': |
|
145 | 166 | return writes_xml(nb, **kwargs) |
|
146 | 167 | elif format == 'json': |
|
147 | 168 | return writes_json(nb, **kwargs) |
|
148 | 169 | elif format == 'py': |
|
149 | 170 | return writes_py(nb, **kwargs) |
|
150 | 171 | else: |
|
151 | 172 | raise NBFormatError('Unsupported format: %s' % format) |
|
152 | 173 | |
|
153 | 174 | |
|
154 | 175 | def read(fp, format, **kwargs): |
|
155 | 176 | """Read a notebook from a file and return the NotebookNode object. |
|
156 | 177 | |
|
157 | 178 | This function properly handles notebooks of any version. The notebook |
|
158 | 179 | returned will always be in the current version's format. |
|
159 | 180 | |
|
160 | 181 | Parameters |
|
161 | 182 | ---------- |
|
162 | 183 | fp : file |
|
163 | 184 | Any file-like object with a read method. |
|
164 | 185 | format : ('xml','json','py') |
|
165 | 186 | The format that the string is in. |
|
166 | 187 | |
|
167 | 188 | Returns |
|
168 | 189 | ------- |
|
169 | 190 | nb : NotebookNode |
|
170 | 191 | The notebook that was read. |
|
171 | 192 | """ |
|
172 | 193 | return reads(fp.read(), format, **kwargs) |
|
173 | 194 | |
|
174 | 195 | |
|
175 | 196 | def write(nb, fp, format, **kwargs): |
|
176 | 197 | """Write a notebook to a file in a given format in the current nbformat version. |
|
177 | 198 | |
|
178 | 199 | This function always writes the notebook in the current nbformat version. |
|
179 | 200 | |
|
180 | 201 | Parameters |
|
181 | 202 | ---------- |
|
182 | 203 | nb : NotebookNode |
|
183 | 204 | The notebook to write. |
|
184 | 205 | fp : file |
|
185 | 206 | Any file-like object with a write method. |
|
186 | 207 | format : ('xml','json','py') |
|
187 | 208 | The format to write the notebook in. |
|
188 | 209 | |
|
189 | 210 | Returns |
|
190 | 211 | ------- |
|
191 | 212 | s : str |
|
192 | 213 | The notebook string. |
|
193 | 214 | """ |
|
194 | 215 | return fp.write(writes(nb, format, **kwargs)) |
|
195 | 216 | |
|
196 | 217 |
@@ -1,12 +1,24 b'' | |||
|
1 | """The main module for the v1 notebook format.""" | |
|
2 | ||
|
3 | #----------------------------------------------------------------------------- | |
|
4 | # Copyright (C) 2008-2011 The IPython Development Team | |
|
5 | # | |
|
6 | # Distributed under the terms of the BSD License. The full license is in | |
|
7 | # the file COPYING, distributed as part of this software. | |
|
8 | #----------------------------------------------------------------------------- | |
|
9 | ||
|
10 | #----------------------------------------------------------------------------- | |
|
11 | # Imports | |
|
12 | #----------------------------------------------------------------------------- | |
|
1 | 13 | |
|
2 | 14 | from .nbbase import ( |
|
3 | 15 | NotebookNode, |
|
4 | 16 | new_code_cell, new_text_cell, new_notebook |
|
5 | 17 | ) |
|
6 | 18 | |
|
7 | 19 | from .nbjson import reads as reads_json, writes as writes_json |
|
8 | 20 | from .nbjson import reads as read_json, writes as write_json |
|
9 | 21 | from .nbjson import to_notebook as to_notebook_json |
|
10 | 22 | |
|
11 | 23 | from .convert import convert_to_this_nbformat |
|
12 | 24 |
@@ -1,5 +1,16 b'' | |||
|
1 | """Convert notebook to the v1 format.""" | |
|
1 | 2 | |
|
3 | #----------------------------------------------------------------------------- | |
|
4 | # Copyright (C) 2008-2011 The IPython Development Team | |
|
5 | # | |
|
6 | # Distributed under the terms of the BSD License. The full license is in | |
|
7 | # the file COPYING, distributed as part of this software. | |
|
8 | #----------------------------------------------------------------------------- | |
|
9 | ||
|
10 | #----------------------------------------------------------------------------- | |
|
11 | # Code | |
|
12 | #----------------------------------------------------------------------------- | |
|
2 | 13 | |
|
3 | 14 | def convert_to_this_nbformat(nb, orig_version=None): |
|
4 | 15 | raise ValueError('Cannot convert to v1 notebook format') |
|
5 | 16 |
@@ -1,53 +1,72 b'' | |||
|
1 |
"""The basic dict based notebook format. |
|
|
1 | """The basic dict based notebook format. | |
|
2 | ||
|
3 | Authors: | |
|
4 | ||
|
5 | * Brian Granger | |
|
6 | """ | |
|
7 | ||
|
8 | #----------------------------------------------------------------------------- | |
|
9 | # Copyright (C) 2008-2011 The IPython Development Team | |
|
10 | # | |
|
11 | # Distributed under the terms of the BSD License. The full license is in | |
|
12 | # the file COPYING, distributed as part of this software. | |
|
13 | #----------------------------------------------------------------------------- | |
|
14 | ||
|
15 | #----------------------------------------------------------------------------- | |
|
16 | # Imports | |
|
17 | #----------------------------------------------------------------------------- | |
|
2 | 18 | |
|
3 | 19 | import pprint |
|
4 | 20 | import uuid |
|
5 | 21 | |
|
6 | 22 | from IPython.utils.ipstruct import Struct |
|
7 | 23 | |
|
24 | #----------------------------------------------------------------------------- | |
|
25 | # Code | |
|
26 | #----------------------------------------------------------------------------- | |
|
8 | 27 | |
|
9 | 28 | class NotebookNode(Struct): |
|
10 | 29 | pass |
|
11 | 30 | |
|
12 | 31 | |
|
13 | 32 | def from_dict(d): |
|
14 | 33 | if isinstance(d, dict): |
|
15 | 34 | newd = NotebookNode() |
|
16 | 35 | for k,v in d.items(): |
|
17 | 36 | newd[k] = from_dict(v) |
|
18 | 37 | return newd |
|
19 | 38 | elif isinstance(d, (tuple, list)): |
|
20 | 39 | return [from_dict(i) for i in d] |
|
21 | 40 | else: |
|
22 | 41 | return d |
|
23 | 42 | |
|
24 | 43 | |
|
25 | 44 | def new_code_cell(code=None, prompt_number=None): |
|
26 | 45 | """Create a new code cell with input and output""" |
|
27 | 46 | cell = NotebookNode() |
|
28 | 47 | cell.cell_type = u'code' |
|
29 | 48 | if code is not None: |
|
30 | 49 | cell.code = unicode(code) |
|
31 | 50 | if prompt_number is not None: |
|
32 | 51 | cell.prompt_number = int(prompt_number) |
|
33 | 52 | return cell |
|
34 | 53 | |
|
35 | 54 | |
|
36 | 55 | def new_text_cell(text=None): |
|
37 | 56 | """Create a new text cell.""" |
|
38 | 57 | cell = NotebookNode() |
|
39 | 58 | if text is not None: |
|
40 | 59 | cell.text = unicode(text) |
|
41 | 60 | cell.cell_type = u'text' |
|
42 | 61 | return cell |
|
43 | 62 | |
|
44 | 63 | |
|
45 | 64 | def new_notebook(cells=None): |
|
46 | 65 | """Create a notebook by name, id and a list of worksheets.""" |
|
47 | 66 | nb = NotebookNode() |
|
48 | 67 | if cells is not None: |
|
49 | 68 | nb.cells = cells |
|
50 | 69 | else: |
|
51 | 70 | nb.cells = [] |
|
52 | 71 | return nb |
|
53 | 72 |
@@ -1,35 +1,54 b'' | |||
|
1 |
"""Read and write notebooks in JSON format. |
|
|
1 | """Read and write notebooks in JSON format. | |
|
2 | ||
|
3 | Authors: | |
|
4 | ||
|
5 | * Brian Granger | |
|
6 | """ | |
|
7 | ||
|
8 | #----------------------------------------------------------------------------- | |
|
9 | # Copyright (C) 2008-2011 The IPython Development Team | |
|
10 | # | |
|
11 | # Distributed under the terms of the BSD License. The full license is in | |
|
12 | # the file COPYING, distributed as part of this software. | |
|
13 | #----------------------------------------------------------------------------- | |
|
14 | ||
|
15 | #----------------------------------------------------------------------------- | |
|
16 | # Imports | |
|
17 | #----------------------------------------------------------------------------- | |
|
2 | 18 | |
|
3 | 19 | from base64 import encodestring |
|
4 | 20 | from .rwbase import NotebookReader, NotebookWriter |
|
5 | 21 | from .nbbase import from_dict |
|
6 | 22 | import json |
|
7 | 23 | |
|
24 | #----------------------------------------------------------------------------- | |
|
25 | # Code | |
|
26 | #----------------------------------------------------------------------------- | |
|
8 | 27 | |
|
9 | 28 | class JSONReader(NotebookReader): |
|
10 | 29 | |
|
11 | 30 | def reads(self, s, **kwargs): |
|
12 | 31 | nb = json.loads(s, **kwargs) |
|
13 | 32 | return self.to_notebook(nb, **kwargs) |
|
14 | 33 | |
|
15 | 34 | def to_notebook(self, d, **kwargs): |
|
16 | 35 | """Convert from a raw JSON dict to a nested NotebookNode structure.""" |
|
17 | 36 | return from_dict(d) |
|
18 | 37 | |
|
19 | 38 | |
|
20 | 39 | class JSONWriter(NotebookWriter): |
|
21 | 40 | |
|
22 | 41 | def writes(self, nb, **kwargs): |
|
23 | 42 | kwargs['indent'] = 4 |
|
24 | 43 | return json.dumps(nb, **kwargs) |
|
25 | 44 | |
|
26 | 45 | |
|
27 | 46 | _reader = JSONReader() |
|
28 | 47 | _writer = JSONWriter() |
|
29 | 48 | |
|
30 | 49 | reads = _reader.reads |
|
31 | 50 | read = _reader.read |
|
32 | 51 | to_notebook = _reader.to_notebook |
|
33 | 52 | write = _writer.write |
|
34 | 53 | writes = _writer.writes |
|
35 | 54 |
@@ -1,26 +1,47 b'' | |||
|
1 | """Base classes and function for readers and writers. | |
|
2 | ||
|
3 | Authors: | |
|
4 | ||
|
5 | * Brian Granger | |
|
6 | """ | |
|
7 | ||
|
8 | #----------------------------------------------------------------------------- | |
|
9 | # Copyright (C) 2008-2011 The IPython Development Team | |
|
10 | # | |
|
11 | # Distributed under the terms of the BSD License. The full license is in | |
|
12 | # the file COPYING, distributed as part of this software. | |
|
13 | #----------------------------------------------------------------------------- | |
|
14 | ||
|
15 | #----------------------------------------------------------------------------- | |
|
16 | # Imports | |
|
17 | #----------------------------------------------------------------------------- | |
|
18 | ||
|
1 | 19 | from base64 import encodestring, decodestring |
|
2 | 20 | |
|
21 | #----------------------------------------------------------------------------- | |
|
22 | # Code | |
|
23 | #----------------------------------------------------------------------------- | |
|
3 | 24 | |
|
4 | 25 | class NotebookReader(object): |
|
5 | 26 | |
|
6 | 27 | def reads(self, s, **kwargs): |
|
7 | 28 | """Read a notebook from a string.""" |
|
8 | 29 | raise NotImplementedError("loads must be implemented in a subclass") |
|
9 | 30 | |
|
10 | 31 | def read(self, fp, **kwargs): |
|
11 | 32 | """Read a notebook from a file like object""" |
|
12 | 33 | return self.reads(fp.read(), **kwargs) |
|
13 | 34 | |
|
14 | 35 | |
|
15 | 36 | class NotebookWriter(object): |
|
16 | 37 | |
|
17 | 38 | def writes(self, nb, **kwargs): |
|
18 | 39 | """Write a notebook to a string.""" |
|
19 | 40 | raise NotImplementedError("loads must be implemented in a subclass") |
|
20 | 41 | |
|
21 | 42 | def write(self, nb, fp, **kwargs): |
|
22 | 43 | """Write a notebook to a file like object""" |
|
23 | 44 | return fp.write(self.writes(nb,**kwargs)) |
|
24 | 45 | |
|
25 | 46 | |
|
26 | 47 |
@@ -1,57 +1,77 b'' | |||
|
1 | """The main API for the v2 notebook format. | |
|
2 | ||
|
3 | Authors: | |
|
4 | ||
|
5 | * Brian Granger | |
|
6 | """ | |
|
7 | ||
|
8 | #----------------------------------------------------------------------------- | |
|
9 | # Copyright (C) 2008-2011 The IPython Development Team | |
|
10 | # | |
|
11 | # Distributed under the terms of the BSD License. The full license is in | |
|
12 | # the file COPYING, distributed as part of this software. | |
|
13 | #----------------------------------------------------------------------------- | |
|
14 | ||
|
15 | #----------------------------------------------------------------------------- | |
|
16 | # Imports | |
|
17 | #----------------------------------------------------------------------------- | |
|
1 | 18 | |
|
2 | 19 | from .nbbase import ( |
|
3 | 20 | NotebookNode, |
|
4 | 21 | new_code_cell, new_text_cell, new_notebook, new_output, new_worksheet |
|
5 | 22 | ) |
|
6 | 23 | |
|
7 | 24 | from .nbjson import reads as reads_json, writes as writes_json |
|
8 | 25 | from .nbjson import reads as read_json, writes as write_json |
|
9 | 26 | from .nbjson import to_notebook as to_notebook_json |
|
10 | 27 | |
|
11 | 28 | from .nbxml import reads as reads_xml, writes as writes_xml |
|
12 | 29 | from .nbxml import reads as read_xml, writes as write_xml |
|
13 | 30 | from .nbxml import to_notebook as to_notebook_xml |
|
14 | 31 | |
|
15 | 32 | from .nbpy import reads as reads_py, writes as writes_py |
|
16 | 33 | from .nbpy import reads as read_py, writes as write_py |
|
17 | 34 | from .nbpy import to_notebook as to_notebook_py |
|
18 | 35 | |
|
19 | 36 | from .convert import convert_to_this_nbformat |
|
20 | 37 | |
|
38 | #----------------------------------------------------------------------------- | |
|
39 | # Code | |
|
40 | #----------------------------------------------------------------------------- | |
|
21 | 41 | |
|
22 | 42 | def parse_filename(fname): |
|
23 | 43 | """Parse a notebook filename. |
|
24 | 44 | |
|
25 | 45 | This function takes a notebook filename and returns the notebook |
|
26 | 46 | format (xml/json/py) and the notebook name. This logic can be |
|
27 | 47 | summarized as follows: |
|
28 | 48 | |
|
29 | 49 | * notebook.ipynb -> (notebook.ipynb, notebook, xml) |
|
30 | 50 | * notebook.json -> (notebook.json, notebook, json) |
|
31 | 51 | * notebook.py -> (notebook.py, notebook, py) |
|
32 | 52 | * notebook -> (notebook.ipynb, notebook, xml) |
|
33 | 53 | |
|
34 | 54 | Parameters |
|
35 | 55 | ---------- |
|
36 | 56 | fname : unicode |
|
37 | 57 | The notebook filename. The filename can use a specific filename |
|
38 | 58 | extention (.ipynb, .json, .py) or none, in which case .ipynb will |
|
39 | 59 | be assumed. |
|
40 | 60 | |
|
41 | 61 | Returns |
|
42 | 62 | ------- |
|
43 | 63 | (fname, name, format) : (unicode, unicode, unicode) |
|
44 | 64 | The filename, notebook name and format. |
|
45 | 65 | """ |
|
46 | 66 | if fname.endswith(u'.ipynb'): |
|
47 | 67 | format = u'xml' |
|
48 | 68 | elif fname.endswith(u'.json'): |
|
49 | 69 | format = u'json' |
|
50 | 70 | elif fname.endswith(u'.py'): |
|
51 | 71 | format = u'py' |
|
52 | 72 | else: |
|
53 | 73 | fname = fname + u'.ipynb' |
|
54 | 74 | format = u'xml' |
|
55 | 75 | name = fname.split('.')[0] |
|
56 | 76 | return fname, name, format |
|
57 | 77 |
@@ -1,20 +1,50 b'' | |||
|
1 | """Code for converting notebooks to and from the v2 format. | |
|
2 | ||
|
3 | Authors: | |
|
4 | ||
|
5 | * Brian Granger | |
|
6 | """ | |
|
7 | ||
|
8 | #----------------------------------------------------------------------------- | |
|
9 | # Copyright (C) 2008-2011 The IPython Development Team | |
|
10 | # | |
|
11 | # Distributed under the terms of the BSD License. The full license is in | |
|
12 | # the file COPYING, distributed as part of this software. | |
|
13 | #----------------------------------------------------------------------------- | |
|
14 | ||
|
15 | #----------------------------------------------------------------------------- | |
|
16 | # Imports | |
|
17 | #----------------------------------------------------------------------------- | |
|
18 | ||
|
1 | 19 | from .nbbase import ( |
|
2 | 20 | new_code_cell, new_text_cell, new_worksheet, new_notebook, new_output |
|
3 | 21 | ) |
|
4 | 22 | |
|
23 | #----------------------------------------------------------------------------- | |
|
24 | # Code | |
|
25 | #----------------------------------------------------------------------------- | |
|
26 | ||
|
5 | 27 | def convert_to_this_nbformat(nb, orig_version=1): |
|
28 | """Convert a notebook to the v2 format. | |
|
29 | ||
|
30 | Parameters | |
|
31 | ---------- | |
|
32 | nb : NotebookNode | |
|
33 | The Python representation of the notebook to convert. | |
|
34 | orig_version : int | |
|
35 | The original version of the notebook to convert. | |
|
36 | """ | |
|
6 | 37 | if orig_version == 1: |
|
7 | 38 | newnb = new_notebook() |
|
8 | 39 | ws = new_worksheet() |
|
9 | 40 | for cell in nb.cells: |
|
10 | 41 | if cell.cell_type == u'code': |
|
11 | 42 | newcell = new_code_cell(input=cell.get('code'),prompt_number=cell.get('prompt_number')) |
|
12 | 43 | elif cell.cell_type == u'text': |
|
13 | 44 | newcell = new_text_cell(u'markdown',source=cell.get('text')) |
|
14 | 45 | ws.cells.append(newcell) |
|
15 | 46 | newnb.worksheets.append(ws) |
|
16 | 47 | return newnb |
|
17 | 48 | else: |
|
18 | 49 | raise ValueError('Cannot convert a notebook from v%s to v2' % orig_version) |
|
19 | 50 | |
|
20 |
@@ -1,132 +1,156 b'' | |||
|
1 |
"""The basic dict based notebook format. |
|
|
1 | """The basic dict based notebook format. | |
|
2 | ||
|
3 | The Python representation of a notebook is a nested structure of | |
|
4 | dictionary subclasses that support attribute access | |
|
5 | (IPython.utils.ipstruct.Struct). The functions in this module are merely | |
|
6 | helpers to build the structs in the right form. | |
|
7 | ||
|
8 | Authors: | |
|
9 | ||
|
10 | * Brian Granger | |
|
11 | """ | |
|
12 | ||
|
13 | #----------------------------------------------------------------------------- | |
|
14 | # Copyright (C) 2008-2011 The IPython Development Team | |
|
15 | # | |
|
16 | # Distributed under the terms of the BSD License. The full license is in | |
|
17 | # the file COPYING, distributed as part of this software. | |
|
18 | #----------------------------------------------------------------------------- | |
|
19 | ||
|
20 | #----------------------------------------------------------------------------- | |
|
21 | # Imports | |
|
22 | #----------------------------------------------------------------------------- | |
|
2 | 23 | |
|
3 | 24 | import pprint |
|
4 | 25 | import uuid |
|
5 | 26 | |
|
6 | 27 | from IPython.utils.ipstruct import Struct |
|
7 | 28 | |
|
29 | #----------------------------------------------------------------------------- | |
|
30 | # Code | |
|
31 | #----------------------------------------------------------------------------- | |
|
8 | 32 | |
|
9 | 33 | class NotebookNode(Struct): |
|
10 | 34 | pass |
|
11 | 35 | |
|
12 | 36 | |
|
13 | 37 | def from_dict(d): |
|
14 | 38 | if isinstance(d, dict): |
|
15 | 39 | newd = NotebookNode() |
|
16 | 40 | for k,v in d.items(): |
|
17 | 41 | newd[k] = from_dict(v) |
|
18 | 42 | return newd |
|
19 | 43 | elif isinstance(d, (tuple, list)): |
|
20 | 44 | return [from_dict(i) for i in d] |
|
21 | 45 | else: |
|
22 | 46 | return d |
|
23 | 47 | |
|
24 | 48 | |
|
25 | 49 | def new_output(output_type=None, output_text=None, output_png=None, |
|
26 | 50 | output_html=None, output_svg=None, output_latex=None, output_json=None, |
|
27 | 51 | output_javascript=None, output_jpeg=None, prompt_number=None, |
|
28 | 52 | etype=None, evalue=None, traceback=None): |
|
29 | 53 | """Create a new code cell with input and output""" |
|
30 | 54 | output = NotebookNode() |
|
31 | 55 | if output_type is not None: |
|
32 | 56 | output.output_type = unicode(output_type) |
|
33 | 57 | |
|
34 | 58 | if output_type != 'pyerr': |
|
35 | 59 | if output_text is not None: |
|
36 | 60 | output.text = unicode(output_text) |
|
37 | 61 | if output_png is not None: |
|
38 | 62 | output.png = bytes(output_png) |
|
39 | 63 | if output_jpeg is not None: |
|
40 | 64 | output.jpeg = bytes(output_jpeg) |
|
41 | 65 | if output_html is not None: |
|
42 | 66 | output.html = unicode(output_html) |
|
43 | 67 | if output_svg is not None: |
|
44 | 68 | output.svg = unicode(output_svg) |
|
45 | 69 | if output_latex is not None: |
|
46 | 70 | output.latex = unicode(output_latex) |
|
47 | 71 | if output_json is not None: |
|
48 | 72 | output.json = unicode(output_json) |
|
49 | 73 | if output_javascript is not None: |
|
50 | 74 | output.javascript = unicode(output_javascript) |
|
51 | 75 | |
|
52 | 76 | if output_type == u'pyout': |
|
53 | 77 | if prompt_number is not None: |
|
54 | 78 | output.prompt_number = int(prompt_number) |
|
55 | 79 | |
|
56 | 80 | if output_type == u'pyerr': |
|
57 | 81 | if etype is not None: |
|
58 | 82 | output.etype = unicode(etype) |
|
59 | 83 | if evalue is not None: |
|
60 | 84 | output.evalue = unicode(evalue) |
|
61 | 85 | if traceback is not None: |
|
62 | 86 | output.traceback = [unicode(frame) for frame in list(traceback)] |
|
63 | 87 | |
|
64 | 88 | return output |
|
65 | 89 | |
|
66 | 90 | |
|
67 | 91 | def new_code_cell(input=None, prompt_number=None, outputs=None, |
|
68 | 92 | language=u'python', collapsed=False): |
|
69 | 93 | """Create a new code cell with input and output""" |
|
70 | 94 | cell = NotebookNode() |
|
71 | 95 | cell.cell_type = u'code' |
|
72 | 96 | if language is not None: |
|
73 | 97 | cell.language = unicode(language) |
|
74 | 98 | if input is not None: |
|
75 | 99 | cell.input = unicode(input) |
|
76 | 100 | if prompt_number is not None: |
|
77 | 101 | cell.prompt_number = int(prompt_number) |
|
78 | 102 | if outputs is None: |
|
79 | 103 | cell.outputs = [] |
|
80 | 104 | else: |
|
81 | 105 | cell.outputs = outputs |
|
82 | 106 | if collapsed is not None: |
|
83 | 107 | cell.collapsed = bool(collapsed) |
|
84 | 108 | |
|
85 | 109 | return cell |
|
86 | 110 | |
|
87 | 111 | def new_text_cell(cell_type, source=None, rendered=None): |
|
88 | 112 | """Create a new text cell.""" |
|
89 | 113 | cell = NotebookNode() |
|
90 | 114 | if source is not None: |
|
91 | 115 | cell.source = unicode(source) |
|
92 | 116 | if rendered is not None: |
|
93 | 117 | cell.rendered = unicode(rendered) |
|
94 | 118 | cell.cell_type = cell_type |
|
95 | 119 | return cell |
|
96 | 120 | |
|
97 | 121 | |
|
98 | 122 | def new_worksheet(name=None, cells=None): |
|
99 | 123 | """Create a worksheet by name with with a list of cells.""" |
|
100 | 124 | ws = NotebookNode() |
|
101 | 125 | if name is not None: |
|
102 | 126 | ws.name = unicode(name) |
|
103 | 127 | if cells is None: |
|
104 | 128 | ws.cells = [] |
|
105 | 129 | else: |
|
106 | 130 | ws.cells = list(cells) |
|
107 | 131 | return ws |
|
108 | 132 | |
|
109 | 133 | |
|
110 | 134 | def new_notebook(name=None, worksheets=None, author=None, email=None, |
|
111 | 135 | created=None, saved=None, license=None): |
|
112 | 136 | """Create a notebook by name, id and a list of worksheets.""" |
|
113 | 137 | nb = NotebookNode() |
|
114 | 138 | nb.nbformat = 2 |
|
115 | 139 | if name is not None: |
|
116 | 140 | nb.name = unicode(name) |
|
117 | 141 | if worksheets is None: |
|
118 | 142 | nb.worksheets = [] |
|
119 | 143 | else: |
|
120 | 144 | nb.worksheets = list(worksheets) |
|
121 | 145 | if author is not None: |
|
122 | 146 | nb.author = unicode(author) |
|
123 | 147 | if email is not None: |
|
124 | 148 | nb.email = unicode(email) |
|
125 | 149 | if created is not None: |
|
126 | 150 | nb.created = unicode(created) |
|
127 | 151 | if saved is not None: |
|
128 | 152 | nb.saved = unicode(saved) |
|
129 | 153 | if license is not None: |
|
130 | 154 | nb.license = unicode(license) |
|
131 | 155 | return nb |
|
132 | 156 |
@@ -1,43 +1,62 b'' | |||
|
1 |
"""Read and write notebooks in JSON format. |
|
|
1 | """Read and write notebooks in JSON format. | |
|
2 | ||
|
3 | Authors: | |
|
4 | ||
|
5 | * Brian Granger | |
|
6 | """ | |
|
7 | ||
|
8 | #----------------------------------------------------------------------------- | |
|
9 | # Copyright (C) 2008-2011 The IPython Development Team | |
|
10 | # | |
|
11 | # Distributed under the terms of the BSD License. The full license is in | |
|
12 | # the file COPYING, distributed as part of this software. | |
|
13 | #----------------------------------------------------------------------------- | |
|
14 | ||
|
15 | #----------------------------------------------------------------------------- | |
|
16 | # Imports | |
|
17 | #----------------------------------------------------------------------------- | |
|
2 | 18 | |
|
3 | 19 | from base64 import encodestring |
|
4 | 20 | from .nbbase import from_dict |
|
5 | 21 | from .rwbase import NotebookReader, NotebookWriter, base64_decode |
|
6 | 22 | import json |
|
7 | 23 | |
|
24 | #----------------------------------------------------------------------------- | |
|
25 | # Code | |
|
26 | #----------------------------------------------------------------------------- | |
|
8 | 27 | |
|
9 | 28 | class BytesEncoder(json.JSONEncoder): |
|
10 | 29 | def default(self, obj): |
|
11 | 30 | if isinstance(obj, bytes): |
|
12 | 31 | return unicode(encodestring(bytes)) |
|
13 | 32 | return json.JSONEncoder.default(self, obj) |
|
14 | 33 | |
|
15 | 34 | |
|
16 | 35 | class JSONReader(NotebookReader): |
|
17 | 36 | |
|
18 | 37 | def reads(self, s, **kwargs): |
|
19 | 38 | nb = json.loads(s, **kwargs) |
|
20 | 39 | nb = self.to_notebook(nb, **kwargs) |
|
21 | 40 | return nb |
|
22 | 41 | |
|
23 | 42 | def to_notebook(self, d, **kwargs): |
|
24 | 43 | return base64_decode(from_dict(d)) |
|
25 | 44 | |
|
26 | 45 | |
|
27 | 46 | class JSONWriter(NotebookWriter): |
|
28 | 47 | |
|
29 | 48 | def writes(self, nb, **kwargs): |
|
30 | 49 | kwargs['cls'] = BytesEncoder |
|
31 | 50 | kwargs['indent'] = 4 |
|
32 | 51 | return json.dumps(nb, **kwargs) |
|
33 | 52 | |
|
34 | 53 | |
|
35 | 54 | _reader = JSONReader() |
|
36 | 55 | _writer = JSONWriter() |
|
37 | 56 | |
|
38 | 57 | reads = _reader.reads |
|
39 | 58 | read = _reader.read |
|
40 | 59 | to_notebook = _reader.to_notebook |
|
41 | 60 | write = _writer.write |
|
42 | 61 | writes = _writer.writes |
|
43 | 62 |
@@ -1,128 +1,147 b'' | |||
|
1 |
"""Read and write notebooks as regular .py files. |
|
|
1 | """Read and write notebooks as regular .py files. | |
|
2 | ||
|
3 | Authors: | |
|
4 | ||
|
5 | * Brian Granger | |
|
6 | """ | |
|
7 | ||
|
8 | #----------------------------------------------------------------------------- | |
|
9 | # Copyright (C) 2008-2011 The IPython Development Team | |
|
10 | # | |
|
11 | # Distributed under the terms of the BSD License. The full license is in | |
|
12 | # the file COPYING, distributed as part of this software. | |
|
13 | #----------------------------------------------------------------------------- | |
|
14 | ||
|
15 | #----------------------------------------------------------------------------- | |
|
16 | # Imports | |
|
17 | #----------------------------------------------------------------------------- | |
|
2 | 18 | |
|
3 | 19 | from .rwbase import NotebookReader, NotebookWriter |
|
4 | 20 | from .nbbase import new_code_cell, new_text_cell, new_worksheet, new_notebook |
|
5 | 21 | |
|
22 | #----------------------------------------------------------------------------- | |
|
23 | # Code | |
|
24 | #----------------------------------------------------------------------------- | |
|
6 | 25 | |
|
7 | 26 | class PyReaderError(Exception): |
|
8 | 27 | pass |
|
9 | 28 | |
|
10 | 29 | |
|
11 | 30 | class PyReader(NotebookReader): |
|
12 | 31 | |
|
13 | 32 | def reads(self, s, **kwargs): |
|
14 | 33 | return self.to_notebook(s,**kwargs) |
|
15 | 34 | |
|
16 | 35 | def to_notebook(self, s, **kwargs): |
|
17 | 36 | lines = s.splitlines() |
|
18 | 37 | cells = [] |
|
19 | 38 | cell_lines = [] |
|
20 | 39 | state = u'codecell' |
|
21 | 40 | for line in lines: |
|
22 | 41 | if line.startswith(u'# <nbformat>'): |
|
23 | 42 | pass |
|
24 | 43 | elif line.startswith(u'# <codecell>'): |
|
25 | 44 | cell = self.new_cell(state, cell_lines) |
|
26 | 45 | if cell is not None: |
|
27 | 46 | cells.append(cell) |
|
28 | 47 | state = u'codecell' |
|
29 | 48 | cell_lines = [] |
|
30 | 49 | elif line.startswith(u'# <htmlcell>'): |
|
31 | 50 | cell = self.new_cell(state, cell_lines) |
|
32 | 51 | if cell is not None: |
|
33 | 52 | cells.append(cell) |
|
34 | 53 | state = u'htmlcell' |
|
35 | 54 | cell_lines = [] |
|
36 | 55 | elif line.startswith(u'# <markdowncell>'): |
|
37 | 56 | cell = self.new_cell(state, cell_lines) |
|
38 | 57 | if cell is not None: |
|
39 | 58 | cells.append(cell) |
|
40 | 59 | state = u'markdowncell' |
|
41 | 60 | cell_lines = [] |
|
42 | 61 | else: |
|
43 | 62 | cell_lines.append(line) |
|
44 | 63 | if cell_lines and state == u'codecell': |
|
45 | 64 | cell = self.new_cell(state, cell_lines) |
|
46 | 65 | if cell is not None: |
|
47 | 66 | cells.append(cell) |
|
48 | 67 | ws = new_worksheet(cells=cells) |
|
49 | 68 | nb = new_notebook(worksheets=[ws]) |
|
50 | 69 | return nb |
|
51 | 70 | |
|
52 | 71 | def new_cell(self, state, lines): |
|
53 | 72 | if state == u'codecell': |
|
54 | 73 | input = u'\n'.join(lines) |
|
55 | 74 | input = input.strip(u'\n') |
|
56 | 75 | if input: |
|
57 | 76 | return new_code_cell(input=input) |
|
58 | 77 | elif state == u'htmlcell': |
|
59 | 78 | text = self._remove_comments(lines) |
|
60 | 79 | if text: |
|
61 | 80 | return new_text_cell(u'html',source=text) |
|
62 | 81 | elif state == u'markdowncell': |
|
63 | 82 | text = self._remove_comments(lines) |
|
64 | 83 | if text: |
|
65 | 84 | return new_text_cell(u'markdown',source=text) |
|
66 | 85 | |
|
67 | 86 | def _remove_comments(self, lines): |
|
68 | 87 | new_lines = [] |
|
69 | 88 | for line in lines: |
|
70 | 89 | if line.startswith(u'#'): |
|
71 | 90 | new_lines.append(line[2:]) |
|
72 | 91 | else: |
|
73 | 92 | new_lines.append(line) |
|
74 | 93 | text = u'\n'.join(new_lines) |
|
75 | 94 | text = text.strip(u'\n') |
|
76 | 95 | return text |
|
77 | 96 | |
|
78 | 97 | def split_lines_into_blocks(self, lines): |
|
79 | 98 | if len(lines) == 1: |
|
80 | 99 | yield lines[0] |
|
81 | 100 | raise StopIteration() |
|
82 | 101 | import ast |
|
83 | 102 | source = '\n'.join(lines) |
|
84 | 103 | code = ast.parse(source) |
|
85 | 104 | starts = [x.lineno-1 for x in code.body] |
|
86 | 105 | for i in range(len(starts)-1): |
|
87 | 106 | yield '\n'.join(lines[starts[i]:starts[i+1]]).strip('\n') |
|
88 | 107 | yield '\n'.join(lines[starts[-1]:]).strip('\n') |
|
89 | 108 | |
|
90 | 109 | |
|
91 | 110 | class PyWriter(NotebookWriter): |
|
92 | 111 | |
|
93 | 112 | def writes(self, nb, **kwargs): |
|
94 | 113 | lines = [] |
|
95 | 114 | lines.extend([u'# <nbformat>2</nbformat>','']) |
|
96 | 115 | for ws in nb.worksheets: |
|
97 | 116 | for cell in ws.cells: |
|
98 | 117 | if cell.cell_type == u'code': |
|
99 | 118 | input = cell.get(u'input') |
|
100 | 119 | if input is not None: |
|
101 | 120 | lines.extend([u'# <codecell>',u'']) |
|
102 | 121 | lines.extend(input.splitlines()) |
|
103 | 122 | lines.append(u'') |
|
104 | 123 | elif cell.cell_type == u'html': |
|
105 | 124 | input = cell.get(u'source') |
|
106 | 125 | if input is not None: |
|
107 | 126 | lines.extend([u'# <htmlcell>',u'']) |
|
108 | 127 | lines.extend([u'# ' + line for line in input.splitlines()]) |
|
109 | 128 | lines.append(u'') |
|
110 | 129 | elif cell.cell_type == u'markdown': |
|
111 | 130 | input = cell.get(u'source') |
|
112 | 131 | if input is not None: |
|
113 | 132 | lines.extend([u'# <markdowncell>',u'']) |
|
114 | 133 | lines.extend([u'# ' + line for line in input.splitlines()]) |
|
115 | 134 | lines.append(u'') |
|
116 | 135 | lines.append('') |
|
117 | 136 | return unicode('\n'.join(lines)) |
|
118 | 137 | |
|
119 | 138 | |
|
120 | 139 | _reader = PyReader() |
|
121 | 140 | _writer = PyWriter() |
|
122 | 141 | |
|
123 | 142 | reads = _reader.reads |
|
124 | 143 | read = _reader.read |
|
125 | 144 | to_notebook = _reader.to_notebook |
|
126 | 145 | write = _writer.write |
|
127 | 146 | writes = _writer.writes |
|
128 | 147 |
@@ -1,226 +1,246 b'' | |||
|
1 |
"""Read and write notebook files as XML. |
|
|
1 | """Read and write notebook files as XML. | |
|
2 | ||
|
3 | Authors: | |
|
4 | ||
|
5 | * Brian Granger | |
|
6 | """ | |
|
7 | ||
|
8 | #----------------------------------------------------------------------------- | |
|
9 | # Copyright (C) 2008-2011 The IPython Development Team | |
|
10 | # | |
|
11 | # Distributed under the terms of the BSD License. The full license is in | |
|
12 | # the file COPYING, distributed as part of this software. | |
|
13 | #----------------------------------------------------------------------------- | |
|
14 | ||
|
15 | #----------------------------------------------------------------------------- | |
|
16 | # Imports | |
|
17 | #----------------------------------------------------------------------------- | |
|
2 | 18 | |
|
3 | 19 | from base64 import encodestring, decodestring |
|
4 | 20 | from xml.etree import ElementTree as ET |
|
5 | 21 | |
|
6 | 22 | from .rwbase import NotebookReader, NotebookWriter |
|
7 | 23 | from .nbbase import ( |
|
8 | 24 | new_code_cell, new_text_cell, new_worksheet, new_notebook, new_output |
|
9 | 25 | ) |
|
10 | 26 | |
|
27 | #----------------------------------------------------------------------------- | |
|
28 | # Code | |
|
29 | #----------------------------------------------------------------------------- | |
|
30 | ||
|
11 | 31 | def indent(elem, level=0): |
|
12 | 32 | i = "\n" + level*" " |
|
13 | 33 | if len(elem): |
|
14 | 34 | if not elem.text or not elem.text.strip(): |
|
15 | 35 | elem.text = i + " " |
|
16 | 36 | if not elem.tail or not elem.tail.strip(): |
|
17 | 37 | elem.tail = i |
|
18 | 38 | for elem in elem: |
|
19 | 39 | indent(elem, level+1) |
|
20 | 40 | if not elem.tail or not elem.tail.strip(): |
|
21 | 41 | elem.tail = i |
|
22 | 42 | else: |
|
23 | 43 | if level and (not elem.tail or not elem.tail.strip()): |
|
24 | 44 | elem.tail = i |
|
25 | 45 | |
|
26 | 46 | |
|
27 | 47 | def _get_text(e, tag): |
|
28 | 48 | sub_e = e.find(tag) |
|
29 | 49 | if sub_e is None: |
|
30 | 50 | return None |
|
31 | 51 | else: |
|
32 | 52 | return sub_e.text |
|
33 | 53 | |
|
34 | 54 | |
|
35 | 55 | def _set_text(nbnode, attr, parent, tag): |
|
36 | 56 | if attr in nbnode: |
|
37 | 57 | e = ET.SubElement(parent, tag) |
|
38 | 58 | e.text = nbnode[attr] |
|
39 | 59 | |
|
40 | 60 | |
|
41 | 61 | def _get_int(e, tag): |
|
42 | 62 | sub_e = e.find(tag) |
|
43 | 63 | if sub_e is None: |
|
44 | 64 | return None |
|
45 | 65 | else: |
|
46 | 66 | return int(sub_e.text) |
|
47 | 67 | |
|
48 | 68 | |
|
49 | 69 | def _set_int(nbnode, attr, parent, tag): |
|
50 | 70 | if attr in nbnode: |
|
51 | 71 | e = ET.SubElement(parent, tag) |
|
52 | 72 | e.text = unicode(nbnode[attr]) |
|
53 | 73 | |
|
54 | 74 | |
|
55 | 75 | def _get_bool(e, tag): |
|
56 | 76 | sub_e = e.find(tag) |
|
57 | 77 | if sub_e is None: |
|
58 | 78 | return None |
|
59 | 79 | else: |
|
60 | 80 | return bool(int(sub_e.text)) |
|
61 | 81 | |
|
62 | 82 | |
|
63 | 83 | def _set_bool(nbnode, attr, parent, tag): |
|
64 | 84 | if attr in nbnode: |
|
65 | 85 | e = ET.SubElement(parent, tag) |
|
66 | 86 | if nbnode[attr]: |
|
67 | 87 | e.text = u'1' |
|
68 | 88 | else: |
|
69 | 89 | e.text = u'0' |
|
70 | 90 | |
|
71 | 91 | |
|
72 | 92 | def _get_binary(e, tag): |
|
73 | 93 | sub_e = e.find(tag) |
|
74 | 94 | if sub_e is None: |
|
75 | 95 | return None |
|
76 | 96 | else: |
|
77 | 97 | return decodestring(sub_e.text) |
|
78 | 98 | |
|
79 | 99 | |
|
80 | 100 | def _set_binary(nbnode, attr, parent, tag): |
|
81 | 101 | if attr in nbnode: |
|
82 | 102 | e = ET.SubElement(parent, tag) |
|
83 | 103 | e.text = encodestring(nbnode[attr]) |
|
84 | 104 | |
|
85 | 105 | |
|
86 | 106 | class XMLReader(NotebookReader): |
|
87 | 107 | |
|
88 | 108 | def reads(self, s, **kwargs): |
|
89 | 109 | root = ET.fromstring(s) |
|
90 | 110 | return self.to_notebook(root, **kwargs) |
|
91 | 111 | |
|
92 | 112 | def to_notebook(self, root, **kwargs): |
|
93 | 113 | nbname = _get_text(root,u'name') |
|
94 | 114 | nbauthor = _get_text(root,u'author') |
|
95 | 115 | nbemail = _get_text(root,u'email') |
|
96 | 116 | nblicense = _get_text(root,u'license') |
|
97 | 117 | nbcreated = _get_text(root,u'created') |
|
98 | 118 | nbsaved = _get_text(root,u'saved') |
|
99 | 119 | |
|
100 | 120 | worksheets = [] |
|
101 | 121 | for ws_e in root.find(u'worksheets').getiterator(u'worksheet'): |
|
102 | 122 | wsname = _get_text(ws_e,u'name') |
|
103 | 123 | cells = [] |
|
104 | 124 | for cell_e in ws_e.find(u'cells').getiterator(): |
|
105 | 125 | if cell_e.tag == u'codecell': |
|
106 | 126 | input = _get_text(cell_e,u'input') |
|
107 | 127 | prompt_number = _get_int(cell_e,u'prompt_number') |
|
108 | 128 | collapsed = _get_bool(cell_e,u'collapsed') |
|
109 | 129 | language = _get_text(cell_e,u'language') |
|
110 | 130 | outputs = [] |
|
111 | 131 | for output_e in cell_e.find(u'outputs').getiterator(u'output'): |
|
112 | 132 | output_type = _get_text(output_e,u'output_type') |
|
113 | 133 | output_text = _get_text(output_e,u'text') |
|
114 | 134 | output_png = _get_binary(output_e,u'png') |
|
115 | 135 | output_jpeg = _get_binary(output_e,u'jpeg') |
|
116 | 136 | output_svg = _get_text(output_e,u'svg') |
|
117 | 137 | output_html = _get_text(output_e,u'html') |
|
118 | 138 | output_latex = _get_text(output_e,u'latex') |
|
119 | 139 | output_json = _get_text(output_e,u'json') |
|
120 | 140 | output_javascript = _get_text(output_e,u'javascript') |
|
121 | 141 | |
|
122 | 142 | out_prompt_number = _get_int(output_e,u'prompt_number') |
|
123 | 143 | etype = _get_text(output_e,u'etype') |
|
124 | 144 | evalue = _get_text(output_e,u'evalue') |
|
125 | 145 | traceback = [] |
|
126 | 146 | traceback_e = output_e.find(u'traceback') |
|
127 | 147 | if traceback_e is not None: |
|
128 | 148 | for frame_e in traceback_e.getiterator(u'frame'): |
|
129 | 149 | traceback.append(frame_e.text) |
|
130 | 150 | if len(traceback) == 0: |
|
131 | 151 | traceback = None |
|
132 | 152 | output = new_output(output_type=output_type,output_png=output_png, |
|
133 | 153 | output_text=output_text, output_svg=output_svg, |
|
134 | 154 | output_html=output_html, output_latex=output_latex, |
|
135 | 155 | output_json=output_json, output_javascript=output_javascript, |
|
136 | 156 | output_jpeg=output_jpeg, prompt_number=out_prompt_number, |
|
137 | 157 | etype=etype, evalue=evalue, traceback=traceback |
|
138 | 158 | ) |
|
139 | 159 | outputs.append(output) |
|
140 | 160 | cc = new_code_cell(input=input,prompt_number=prompt_number, |
|
141 | 161 | language=language,outputs=outputs,collapsed=collapsed) |
|
142 | 162 | cells.append(cc) |
|
143 | 163 | if cell_e.tag == u'htmlcell': |
|
144 | 164 | source = _get_text(cell_e,u'source') |
|
145 | 165 | rendered = _get_text(cell_e,u'rendered') |
|
146 | 166 | cells.append(new_text_cell(u'html', source=source, rendered=rendered)) |
|
147 | 167 | if cell_e.tag == u'markdowncell': |
|
148 | 168 | source = _get_text(cell_e,u'source') |
|
149 | 169 | rendered = _get_text(cell_e,u'rendered') |
|
150 | 170 | cells.append(new_text_cell(u'markdown', source=source, rendered=rendered)) |
|
151 | 171 | ws = new_worksheet(name=wsname,cells=cells) |
|
152 | 172 | worksheets.append(ws) |
|
153 | 173 | |
|
154 | 174 | nb = new_notebook(name=nbname,worksheets=worksheets,author=nbauthor, |
|
155 | 175 | email=nbemail,license=nblicense,saved=nbsaved,created=nbcreated) |
|
156 | 176 | return nb |
|
157 | 177 | |
|
158 | 178 | |
|
159 | 179 | class XMLWriter(NotebookWriter): |
|
160 | 180 | |
|
161 | 181 | def writes(self, nb, **kwargs): |
|
162 | 182 | nb_e = ET.Element(u'notebook') |
|
163 | 183 | _set_text(nb,u'name',nb_e,u'name') |
|
164 | 184 | _set_text(nb,u'author',nb_e,u'author') |
|
165 | 185 | _set_text(nb,u'email',nb_e,u'email') |
|
166 | 186 | _set_text(nb,u'license',nb_e,u'license') |
|
167 | 187 | _set_text(nb,u'created',nb_e,u'created') |
|
168 | 188 | _set_text(nb,u'saved',nb_e,u'saved') |
|
169 | 189 | _set_int(nb,u'nbformat',nb_e,u'nbformat') |
|
170 | 190 | wss_e = ET.SubElement(nb_e,u'worksheets') |
|
171 | 191 | for ws in nb.worksheets: |
|
172 | 192 | ws_e = ET.SubElement(wss_e, u'worksheet') |
|
173 | 193 | _set_text(ws,u'name',ws_e,u'name') |
|
174 | 194 | cells_e = ET.SubElement(ws_e,u'cells') |
|
175 | 195 | for cell in ws.cells: |
|
176 | 196 | cell_type = cell.cell_type |
|
177 | 197 | if cell_type == u'code': |
|
178 | 198 | cell_e = ET.SubElement(cells_e, u'codecell') |
|
179 | 199 | _set_text(cell,u'input',cell_e,u'input') |
|
180 | 200 | _set_text(cell,u'language',cell_e,u'language') |
|
181 | 201 | _set_int(cell,u'prompt_number',cell_e,u'prompt_number') |
|
182 | 202 | _set_bool(cell,u'collapsed',cell_e,u'collapsed') |
|
183 | 203 | outputs_e = ET.SubElement(cell_e, u'outputs') |
|
184 | 204 | for output in cell.outputs: |
|
185 | 205 | output_e = ET.SubElement(outputs_e, u'output') |
|
186 | 206 | _set_text(output,u'output_type',output_e,u'output_type') |
|
187 | 207 | _set_text(output,u'text',output_e,u'text') |
|
188 | 208 | _set_binary(output,u'png',output_e,u'png') |
|
189 | 209 | _set_binary(output,u'jpeg',output_e,u'jpeg') |
|
190 | 210 | _set_text(output,u'html',output_e,u'html') |
|
191 | 211 | _set_text(output,u'svg',output_e,u'svg') |
|
192 | 212 | _set_text(output,u'latex',output_e,u'latex') |
|
193 | 213 | _set_text(output,u'json',output_e,u'json') |
|
194 | 214 | _set_text(output,u'javascript',output_e,u'javascript') |
|
195 | 215 | _set_int(output,u'prompt_number',output_e,u'prompt_number') |
|
196 | 216 | _set_text(output,u'etype',output_e,u'etype') |
|
197 | 217 | _set_text(output,u'evalue',output_e,u'evalue') |
|
198 | 218 | if u'traceback' in output: |
|
199 | 219 | tb_e = ET.SubElement(output_e, u'traceback') |
|
200 | 220 | for frame in output.traceback: |
|
201 | 221 | frame_e = ET.SubElement(tb_e, u'frame') |
|
202 | 222 | frame_e.text = frame |
|
203 | 223 | elif cell_type == u'html': |
|
204 | 224 | cell_e = ET.SubElement(cells_e, u'htmlcell') |
|
205 | 225 | _set_text(cell,u'source',cell_e,u'source') |
|
206 | 226 | _set_text(cell,u'rendered',cell_e,u'rendered') |
|
207 | 227 | elif cell_type == u'markdown': |
|
208 | 228 | cell_e = ET.SubElement(cells_e, u'markdowncell') |
|
209 | 229 | _set_text(cell,u'source',cell_e,u'source') |
|
210 | 230 | _set_text(cell,u'rendered',cell_e,u'rendered') |
|
211 | 231 | |
|
212 | 232 | indent(nb_e) |
|
213 | 233 | txt = ET.tostring(nb_e, encoding="utf-8") |
|
214 | 234 | txt = '<?xml version="1.0" encoding="utf-8"?>\n' + txt |
|
215 | 235 | return txt |
|
216 | 236 | |
|
217 | 237 | |
|
218 | 238 | _reader = XMLReader() |
|
219 | 239 | _writer = XMLWriter() |
|
220 | 240 | |
|
221 | 241 | reads = _reader.reads |
|
222 | 242 | read = _reader.read |
|
223 | 243 | to_notebook = _reader.to_notebook |
|
224 | 244 | write = _writer.write |
|
225 | 245 | writes = _writer.writes |
|
226 | 246 |
@@ -1,50 +1,74 b'' | |||
|
1 | """Base classes and utilities for readers and writers. | |
|
2 | ||
|
3 | Authors: | |
|
4 | ||
|
5 | * Brian Granger | |
|
6 | """ | |
|
7 | ||
|
8 | #----------------------------------------------------------------------------- | |
|
9 | # Copyright (C) 2008-2011 The IPython Development Team | |
|
10 | # | |
|
11 | # Distributed under the terms of the BSD License. The full license is in | |
|
12 | # the file COPYING, distributed as part of this software. | |
|
13 | #----------------------------------------------------------------------------- | |
|
14 | ||
|
15 | #----------------------------------------------------------------------------- | |
|
16 | # Imports | |
|
17 | #----------------------------------------------------------------------------- | |
|
18 | ||
|
1 | 19 | from base64 import encodestring, decodestring |
|
2 | 20 | import pprint |
|
3 | 21 | |
|
22 | #----------------------------------------------------------------------------- | |
|
23 | # Code | |
|
24 | #----------------------------------------------------------------------------- | |
|
25 | ||
|
4 | 26 | def base64_decode(nb): |
|
5 | 27 | """Base64 encode all bytes objects in the notebook.""" |
|
6 | 28 | for ws in nb.worksheets: |
|
7 | 29 | for cell in ws.cells: |
|
8 | 30 | if cell.cell_type == 'code': |
|
9 | 31 | if 'png' in cell: |
|
10 | 32 | cell.png = bytes(decodestring(cell.png)) |
|
11 | 33 | if 'jpeg' in cell: |
|
12 | 34 | cell.jpeg = bytes(decodestring(cell.jpeg)) |
|
13 | 35 | return nb |
|
14 | 36 | |
|
15 | 37 | |
|
16 | 38 | def base64_encode(nb): |
|
17 | 39 | """Base64 decode all binary objects in the notebook.""" |
|
18 | 40 | for ws in nb.worksheets: |
|
19 | 41 | for cell in ws.cells: |
|
20 | 42 | if cell.cell_type == 'code': |
|
21 | 43 | if 'png' in cell: |
|
22 | 44 | cell.png = unicode(encodestring(cell.png)) |
|
23 | 45 | if 'jpeg' in cell: |
|
24 | 46 | cell.jpeg = unicode(encodestring(cell.jpeg)) |
|
25 | 47 | return nb |
|
26 | 48 | |
|
27 | 49 | |
|
28 | 50 | class NotebookReader(object): |
|
51 | """A class for reading notebooks.""" | |
|
29 | 52 | |
|
30 | 53 | def reads(self, s, **kwargs): |
|
31 | 54 | """Read a notebook from a string.""" |
|
32 | 55 | raise NotImplementedError("loads must be implemented in a subclass") |
|
33 | 56 | |
|
34 | 57 | def read(self, fp, **kwargs): |
|
35 | 58 | """Read a notebook from a file like object""" |
|
36 | 59 | return self.read(fp.read(), **kwargs) |
|
37 | 60 | |
|
38 | 61 | |
|
39 | 62 | class NotebookWriter(object): |
|
63 | """A class for writing notebooks.""" | |
|
40 | 64 | |
|
41 | 65 | def writes(self, nb, **kwargs): |
|
42 | 66 | """Write a notebook to a string.""" |
|
43 | 67 | raise NotImplementedError("loads must be implemented in a subclass") |
|
44 | 68 | |
|
45 | 69 | def write(self, nb, fp, **kwargs): |
|
46 | 70 | """Write a notebook to a file like object""" |
|
47 | 71 | return fp.write(self.writes(nb,**kwargs)) |
|
48 | 72 | |
|
49 | 73 | |
|
50 | 74 |
|
1 | NO CONTENT: file was removed |
General Comments 0
You need to be logged in to leave comments.
Login now