##// END OF EJS Templates
Ported the IPython Sphinx directive to 0.11....
Fernando Perez -
Show More
This diff has been collapsed as it changes many lines, (641 lines changed) Show them Hide them
@@ -0,0 +1,641 b''
1 # -*- coding: utf-8 -*-
2 """Sphinx directive to support embedded IPython code.
3
4 This directive allows pasting of entire interactive IPython sessions, prompts
5 and all, and their code will actually get re-executed at doc build time, with
6 all prompts renumbered sequentially.
7
8 To enable this directive, simply list it in your Sphinx ``conf.py`` file
9 (making sure the directory where you placed it is visible to sphinx, as is
10 needed for all Sphinx directives).
11
12 By default this directive assumes that your prompts are unchanged IPython ones,
13 but this can be customized. For example, the following code in your Sphinx
14 config file will configure this directive for the following input/output
15 prompts ``Yade [1]:`` and ``-> [1]:``::
16
17 import ipython_directive as id
18 id.rgxin =re.compile(r'(?:In |Yade )\[(\d+)\]:\s?(.*)\s*')
19 id.rgxout=re.compile(r'(?:Out| -> )\[(\d+)\]:\s?(.*)\s*')
20 id.fmtin ='Yade [%d]:'
21 id.fmtout=' -> [%d]:'
22
23 from IPython import Config
24 id.CONFIG = Config(
25 prompt_in1="Yade [\#]:",
26 prompt_in2=" .\D..",
27 prompt_out=" -> [\#]:"
28 )
29 id.reconfig_shell()
30
31 import ipython_console_highlighting as ich
32 ich.IPythonConsoleLexer.input_prompt=
33 re.compile("(Yade \[[0-9]+\]: )|( \.\.\.+:)")
34 ich.IPythonConsoleLexer.output_prompt=
35 re.compile("(( -> )|(Out)\[[0-9]+\]: )|( \.\.\.+:)")
36 ich.IPythonConsoleLexer.continue_prompt=re.compile(" \.\.\.+:")
37
38
39 ToDo
40 ----
41
42 - Turn the ad-hoc test() function into a real test suite.
43 - Break up ipython-specific functionality from matplotlib stuff into better
44 separated code.
45 - Make sure %bookmarks used internally are removed on exit.
46
47
48 Authors
49 -------
50
51 - John D Hunter: orignal author.
52 - Fernando Perez: refactoring, documentation, cleanups, port to 0.11.
53 - VΓ‘clavΕ milauer <eudoxos-AT-arcig.cz>: Prompt generalizations.
54 """
55
56 #-----------------------------------------------------------------------------
57 # Imports
58 #-----------------------------------------------------------------------------
59
60 # Stdlib
61 import cStringIO
62 import imp
63 import os
64 import re
65 import shutil
66 import sys
67 import warnings
68
69 # To keep compatibility with various python versions
70 try:
71 from hashlib import md5
72 except ImportError:
73 from md5 import md5
74
75 # Third-party
76 import matplotlib
77 import sphinx
78 from docutils.parsers.rst import directives
79
80 matplotlib.use('Agg')
81
82 # Our own
83 from IPython import Config, IPythonApp
84 from IPython.utils.genutils import Term, Tee
85
86 #-----------------------------------------------------------------------------
87 # Globals
88 #-----------------------------------------------------------------------------
89
90 sphinx_version = sphinx.__version__.split(".")
91 # The split is necessary for sphinx beta versions where the string is
92 # '6b1'
93 sphinx_version = tuple([int(re.split('[a-z]', x)[0])
94 for x in sphinx_version[:2]])
95
96 COMMENT, INPUT, OUTPUT = range(3)
97 CONFIG = Config()
98 rgxin = re.compile('In \[(\d+)\]:\s?(.*)\s*')
99 rgxout = re.compile('Out\[(\d+)\]:\s?(.*)\s*')
100 fmtin = 'In [%d]:'
101 fmtout = 'Out[%d]:'
102
103 #-----------------------------------------------------------------------------
104 # Functions and class declarations
105 #-----------------------------------------------------------------------------
106 def block_parser(part):
107 """
108 part is a string of ipython text, comprised of at most one
109 input, one ouput, comments, and blank lines. The block parser
110 parses the text into a list of::
111
112 blocks = [ (TOKEN0, data0), (TOKEN1, data1), ...]
113
114 where TOKEN is one of [COMMENT | INPUT | OUTPUT ] and
115 data is, depending on the type of token::
116
117 COMMENT : the comment string
118
119 INPUT: the (DECORATOR, INPUT_LINE, REST) where
120 DECORATOR: the input decorator (or None)
121 INPUT_LINE: the input as string (possibly multi-line)
122 REST : any stdout generated by the input line (not OUTPUT)
123
124
125 OUTPUT: the output string, possibly multi-line
126 """
127
128 block = []
129 lines = part.split('\n')
130 N = len(lines)
131 i = 0
132 decorator = None
133 while 1:
134
135 if i==N:
136 # nothing left to parse -- the last line
137 break
138
139 line = lines[i]
140 i += 1
141 line_stripped = line.strip()
142 if line_stripped.startswith('#'):
143 block.append((COMMENT, line))
144 continue
145
146 if line_stripped.startswith('@'):
147 # we're assuming at most one decorator -- may need to
148 # rethink
149 decorator = line_stripped
150 continue
151
152 # does this look like an input line?
153 matchin = rgxin.match(line)
154 if matchin:
155 lineno, inputline = int(matchin.group(1)), matchin.group(2)
156
157 # the ....: continuation string
158 continuation = ' %s:'%''.join(['.']*(len(str(lineno))+2))
159 Nc = len(continuation)
160 # input lines can continue on for more than one line, if
161 # we have a '\' line continuation char or a function call
162 # echo line 'print'. The input line can only be
163 # terminated by the end of the block or an output line, so
164 # we parse out the rest of the input line if it is
165 # multiline as well as any echo text
166
167 rest = []
168 while i<N:
169
170 # look ahead; if the next line is blank, or a comment, or
171 # an output line, we're done
172
173 nextline = lines[i]
174 matchout = rgxout.match(nextline)
175 #print "nextline=%s, continuation=%s, starts=%s"%(nextline, continuation, nextline.startswith(continuation))
176 if matchout or nextline.startswith('#'):
177 break
178 elif nextline.startswith(continuation):
179 inputline += '\n' + nextline[Nc:]
180 else:
181 rest.append(nextline)
182 i+= 1
183
184 block.append((INPUT, (decorator, inputline, '\n'.join(rest))))
185 continue
186
187 # if it looks like an output line grab all the text to the end
188 # of the block
189 matchout = rgxout.match(line)
190 if matchout:
191 lineno, output = int(matchout.group(1)), matchout.group(2)
192 if i<N-1:
193 output = '\n'.join([output] + lines[i:])
194
195 block.append((OUTPUT, output))
196 break
197
198 return block
199
200
201 class EmbeddedSphinxShell(object):
202 """An embedded IPython instance to run inside Sphinx"""
203
204 def __init__(self):
205
206 self.cout = cStringIO.StringIO()
207 Term.cout = self.cout
208 Term.cerr = self.cout
209
210 # For debugging, so we can see normal output, use this:
211 #Term.cout = genutils.Tee(self.cout, channel='stdout') # dbg
212 #Term.cerr = genutils.Tee(self.cout, channel='stderr') # dbg
213
214 # Create config object for IPython
215 config = Config()
216 config.Global.display_banner = False
217 config.Global.exec_lines = ['import numpy as np',
218 'from pylab import *'
219 ]
220 config.InteractiveShell.autocall = False
221 config.InteractiveShell.autoindent = False
222 config.InteractiveShell.colors = 'NoColor'
223
224 # Merge global config which can be used to override.
225 config._merge(CONFIG)
226
227 # Create and initialize ipython, but don't start its mainloop
228 IP = IPythonApp(override_config=config)
229 IP.initialize()
230
231 # Store a few parts of IPython we'll need.
232 self.IP = IP.shell
233 self.user_ns = self.IP.user_ns
234 self.user_global_ns = self.IP.user_global_ns
235
236 self.input = ''
237 self.output = ''
238
239 self.is_verbatim = False
240 self.is_doctest = False
241 self.is_suppress = False
242
243 # on the first call to the savefig decorator, we'll import
244 # pyplot as plt so we can make a call to the plt.gcf().savefig
245 self._pyplot_imported = False
246
247 # we need bookmark the current dir first so we can save
248 # relative to it
249 self.process_input_line('bookmark ipy_basedir')
250 self.cout.seek(0)
251 self.cout.truncate(0)
252
253 def process_input_line(self, line):
254 """process the input, capturing stdout"""
255 #print "input='%s'"%self.input
256 stdout = sys.stdout
257 try:
258 sys.stdout = self.cout
259 self.IP.push_line(line)
260 finally:
261 sys.stdout = stdout
262
263 # Callbacks for each type of token
264 def process_input(self, data, input_prompt, lineno):
265 """Process data block for INPUT token."""
266 decorator, input, rest = data
267 image_file = None
268 #print 'INPUT:', data # dbg
269 is_verbatim = decorator=='@verbatim' or self.is_verbatim
270 is_doctest = decorator=='@doctest' or self.is_doctest
271 is_suppress = decorator=='@suppress' or self.is_suppress
272 is_savefig = decorator is not None and \
273 decorator.startswith('@savefig')
274
275 input_lines = input.split('\n')
276
277 continuation = ' %s:'%''.join(['.']*(len(str(lineno))+2))
278 Nc = len(continuation)
279
280 if is_savefig:
281 saveargs = decorator.split(' ')
282 filename = saveargs[1]
283 outfile = os.path.join('_static/%s'%filename)
284 # build out an image directive like
285 # .. image:: somefile.png
286 # :width 4in
287 #
288 # from an input like
289 # savefig somefile.png width=4in
290 imagerows = ['.. image:: %s'%outfile]
291
292 for kwarg in saveargs[2:]:
293 arg, val = kwarg.split('=')
294 arg = arg.strip()
295 val = val.strip()
296 imagerows.append(' :%s: %s'%(arg, val))
297
298 image_file = outfile
299 image_directive = '\n'.join(imagerows)
300
301 # TODO: can we get "rest" from ipython
302 #self.process_input_line('\n'.join(input_lines))
303
304 ret = []
305 is_semicolon = False
306
307 for i, line in enumerate(input_lines):
308 if line.endswith(';'):
309 is_semicolon = True
310
311 if i==0:
312 # process the first input line
313 if is_verbatim:
314 self.process_input_line('')
315 else:
316 # only submit the line in non-verbatim mode
317 self.process_input_line(line)
318 formatted_line = '%s %s'%(input_prompt, line)
319 else:
320 # process a continuation line
321 if not is_verbatim:
322 self.process_input_line(line)
323
324 formatted_line = '%s %s'%(continuation, line)
325
326 if not is_suppress:
327 ret.append(formatted_line)
328
329 if not is_suppress:
330 if len(rest.strip()):
331 if is_verbatim:
332 # the "rest" is the standard output of the
333 # input, which needs to be added in
334 # verbatim mode
335 ret.append(rest)
336
337 self.cout.seek(0)
338 output = self.cout.read()
339 if not is_suppress and not is_semicolon:
340 ret.append(output)
341
342 self.cout.truncate(0)
343 return ret, input_lines, output, is_doctest, image_file
344 #print 'OUTPUT', output # dbg
345
346 def process_output(self, data, output_prompt,
347 input_lines, output, is_doctest, image_file):
348 """Process data block for OUTPUT token."""
349 if is_doctest:
350 submitted = data.strip()
351 found = output
352 if found is not None:
353 found = found.strip()
354
355 # XXX - fperez: in 0.11, 'output' never comes with the prompt
356 # in it, just the actual output text. So I think all this code
357 # can be nuked...
358 ## ind = found.find(output_prompt)
359 ## if ind<0:
360 ## e='output prompt="%s" does not match out line=%s' % \
361 ## (output_prompt, found)
362 ## raise RuntimeError(e)
363 ## found = found[len(output_prompt):].strip()
364
365 if found!=submitted:
366 e = ('doctest failure for input_lines="%s" with '
367 'found_output="%s" and submitted output="%s"' %
368 (input_lines, found, submitted) )
369 raise RuntimeError(e)
370 #print 'doctest PASSED for input_lines="%s" with found_output="%s" and submitted output="%s"'%(input_lines, found, submitted)
371
372 def process_comment(self, data):
373 """Process data block for COMMENT token."""
374 if not self.is_suppress:
375 return [data]
376
377 def process_block(self, block):
378 """
379 process block from the block_parser and return a list of processed lines
380 """
381
382 ret = []
383 output = None
384 input_lines = None
385
386 m = rgxin.match(str(self.IP.outputcache.prompt1).strip())
387 lineno = int(m.group(1))
388
389 input_prompt = fmtin%lineno
390 output_prompt = fmtout%lineno
391 image_file = None
392 image_directive = None
393 # XXX - This needs a second refactor. There's too much state being
394 # held globally, which makes for a very awkward interface and large,
395 # hard to test functions. I've already broken this up at least into
396 # three separate processors to isolate the logic better, but this only
397 # serves to highlight the coupling. Next we need to clean it up...
398 for token, data in block:
399 if token==COMMENT:
400 out_data = self.process_comment(data)
401 elif token==INPUT:
402 out_data, input_lines, output, is_doctest, image_file= \
403 self.process_input(data, input_prompt, lineno)
404 elif token==OUTPUT:
405 out_data = \
406 self.process_output(data, output_prompt,
407 input_lines, output, is_doctest,
408 image_file)
409 if out_data:
410 ret.extend(out_data)
411
412 if image_file is not None:
413 self.ensure_pyplot()
414 command = 'plt.gcf().savefig("%s")'%image_file
415 print 'SAVEFIG', command # dbg
416 self.process_input_line('bookmark ipy_thisdir')
417 self.process_input_line('cd -b ipy_basedir')
418 self.process_input_line(command)
419 self.process_input_line('cd -b ipy_thisdir')
420 self.cout.seek(0)
421 self.cout.truncate(0)
422 return ret, image_directive
423
424 def ensure_pyplot(self):
425 if self._pyplot_imported:
426 return
427 self.process_input_line('import matplotlib.pyplot as plt')
428
429 # A global instance used below. XXX: not sure why this can't be created inside
430 # ipython_directive itself.
431 shell = EmbeddedSphinxShell()
432
433 def reconfig_shell():
434 """Called after setting module-level variables to re-instantiate
435 with the set values (since shell is instantiated first at import-time
436 when module variables have default values)"""
437 global shell
438 shell = EmbeddedSphinxShell()
439
440
441 def ipython_directive(name, arguments, options, content, lineno,
442 content_offset, block_text, state, state_machine,
443 ):
444
445 debug = ipython_directive.DEBUG
446 shell.is_suppress = options.has_key('suppress')
447 shell.is_doctest = options.has_key('doctest')
448 shell.is_verbatim = options.has_key('verbatim')
449
450 #print 'ipy', shell.is_suppress, options
451 parts = '\n'.join(content).split('\n\n')
452 lines = ['.. sourcecode:: ipython', '']
453
454 figures = []
455 for part in parts:
456 block = block_parser(part)
457
458 if len(block):
459 rows, figure = shell.process_block(block)
460 for row in rows:
461 lines.extend([' %s'%line for line in row.split('\n')])
462
463 if figure is not None:
464 figures.append(figure)
465
466 for figure in figures:
467 lines.append('')
468 lines.extend(figure.split('\n'))
469 lines.append('')
470
471 #print lines
472 if len(lines)>2:
473 if debug:
474 print '\n'.join(lines)
475 else:
476 #print 'INSERTING %d lines'%len(lines)
477 state_machine.insert_input(
478 lines, state_machine.input_lines.source(0))
479
480 return []
481
482 ipython_directive.DEBUG = False
483 ipython_directive.DEBUG = True # dbg
484
485 # Enable as a proper Sphinx directive
486 def setup(app):
487 setup.app = app
488 options = {'suppress': directives.flag,
489 'doctest': directives.flag,
490 'verbatim': directives.flag,
491 }
492
493 app.add_directive('ipython', ipython_directive, True, (0, 2, 0), **options)
494
495
496 # Simple smoke test, needs to be converted to a proper automatic test.
497 def test():
498
499 examples = [
500 r"""
501 In [9]: pwd
502 Out[9]: '/home/jdhunter/py4science/book'
503
504 In [10]: cd bookdata/
505 /home/jdhunter/py4science/book/bookdata
506
507 In [2]: from pylab import *
508
509 In [2]: ion()
510
511 In [3]: im = imread('stinkbug.png')
512
513 @savefig mystinkbug.png width=4in
514 In [4]: imshow(im)
515 Out[4]: <matplotlib.image.AxesImage object at 0x39ea850>
516
517 """,
518 r"""
519
520 In [1]: x = 'hello world'
521
522 # string methods can be
523 # used to alter the string
524 @doctest
525 In [2]: x.upper()
526 Out[2]: 'HELLO WORLD'
527
528 @verbatim
529 In [3]: x.st<TAB>
530 x.startswith x.strip
531 """,
532 r"""
533
534 In [130]: url = 'http://ichart.finance.yahoo.com/table.csv?s=CROX\
535 .....: &d=9&e=22&f=2009&g=d&a=1&br=8&c=2006&ignore=.csv'
536
537 In [131]: print url.split('&')
538 ['http://ichart.finance.yahoo.com/table.csv?s=CROX', 'd=9', 'e=22', 'f=2009', 'g=d', 'a=1', 'b=8', 'c=2006', 'ignore=.csv']
539
540 In [60]: import urllib
541
542 """,
543 r"""\
544
545 In [133]: import numpy.random
546
547 @suppress
548 In [134]: numpy.random.seed(2358)
549
550 @doctest
551 In [135]: np.random.rand(10,2)
552 Out[135]:
553 array([[ 0.64524308, 0.59943846],
554 [ 0.47102322, 0.8715456 ],
555 [ 0.29370834, 0.74776844],
556 [ 0.99539577, 0.1313423 ],
557 [ 0.16250302, 0.21103583],
558 [ 0.81626524, 0.1312433 ],
559 [ 0.67338089, 0.72302393],
560 [ 0.7566368 , 0.07033696],
561 [ 0.22591016, 0.77731835],
562 [ 0.0072729 , 0.34273127]])
563
564 """,
565
566 r"""
567 In [106]: print x
568 jdh
569
570 In [109]: for i in range(10):
571 .....: print i
572 .....:
573 .....:
574 0
575 1
576 2
577 3
578 4
579 5
580 6
581 7
582 8
583 9
584 """,
585
586 r"""
587
588 In [144]: from pylab import *
589
590 In [145]: ion()
591
592 # use a semicolon to suppress the output
593 @savefig test_hist.png width=4in
594 In [151]: hist(np.random.randn(10000), 100);
595
596
597 @savefig test_plot.png width=4in
598 In [151]: plot(np.random.randn(10000), 'o');
599 """,
600
601 r"""
602 # use a semicolon to suppress the output
603 In [151]: plt.clf()
604
605 @savefig plot_simple.png width=4in
606 In [151]: plot([1,2,3])
607
608 @savefig hist_simple.png width=4in
609 In [151]: hist(np.random.randn(10000), 100);
610
611 """,
612 r"""
613 # update the current fig
614 In [151]: ylabel('number')
615
616 In [152]: title('normal distribution')
617
618
619 @savefig hist_with_text.png
620 In [153]: grid(True)
621
622 """,
623 ]
624
625 #ipython_directive.DEBUG = True # dbg
626 #options = dict(suppress=True) # dbg
627 options = dict()
628 for example in examples:
629 content = example.split('\n')
630 ipython_directive('debug', arguments=None, options=options,
631 content=content, lineno=0,
632 content_offset=None, block_text=None,
633 state=None, state_machine=None,
634 )
635
636 # Run test suite as a script
637 if __name__=='__main__':
638 if not os.path.isdir('_static'):
639 os.mkdir('_static')
640 test()
641 print 'All OK? Check figures in _static/'
@@ -16,10 +16,10 b' IPython is a set of tools for interactive and exploratory computing in Python.'
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17 # Imports
17 # Imports
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19 from __future__ import absolute_import
19
20
20 import os
21 import os
21 import sys
22 import sys
22 from IPython.core import release
23
23
24 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
25 # Setup everything
25 # Setup everything
@@ -38,12 +38,16 b' sys.path.append(os.path.join(os.path.dirname(__file__), "extensions"))'
38 #-----------------------------------------------------------------------------
38 #-----------------------------------------------------------------------------
39
39
40 # In some cases, these are causing circular imports.
40 # In some cases, these are causing circular imports.
41 from IPython.core.iplib import InteractiveShell
41 from .config.loader import Config
42 from IPython.core.embed import embed
42 from .core import release
43 from IPython.core.error import TryNext
43 from .core.application import Application
44 from IPython.testing import test
44 from .core.ipapp import IPythonApp
45 from .core.embed import embed
46 from .core.error import TryNext
47 from .core.iplib import InteractiveShell
48 from .testing import test
45
49
46 from IPython.lib import (
50 from .lib import (
47 enable_wx, disable_wx,
51 enable_wx, disable_wx,
48 enable_gtk, disable_gtk,
52 enable_gtk, disable_gtk,
49 enable_qt4, disable_qt4,
53 enable_qt4, disable_qt4,
@@ -84,27 +84,62 b' app_cl_args = ('
84 )
84 )
85
85
86 class Application(object):
86 class Application(object):
87 """Load a config, construct components and set them running."""
87 """Load a config, construct components and set them running.
88
89 The configuration of an application can be done via four different Config
90 objects, which are loaded and ultimately merged into a single one used from
91 that point on by the app. These are:
92
93 1. default_config: internal defaults, implemented in code.
94 2. file_config: read from the filesystem.
95 3. command_line_config: read from the system's command line flags.
96 4. constructor_config: passed parametrically to the constructor.
97
98 During initialization, 3 is actually read before 2, since at the
99 command-line one may override the location of the file to be read. But the
100 above is the order in which the merge is made.
101
102 There is a final config object can be created and passed to the
103 constructor: override_config. If it exists, this completely overrides the
104 configs 2-4 above (the default is still used to ensure that all needed
105 fields at least are created). This makes it easier to create
106 parametrically (e.g. in testing or sphinx plugins) objects with a known
107 configuration, that are unaffected by whatever arguments may be present in
108 sys.argv or files in the user's various directories.
109 """
88
110
89 name = u'ipython'
111 name = u'ipython'
90 description = 'IPython: an enhanced interactive Python shell.'
112 description = 'IPython: an enhanced interactive Python shell.'
91 #: usage message printed by argparse. If None, auto-generate
113 #: usage message printed by argparse. If None, auto-generate
92 usage = None
114 usage = None
93 config_file_name = u'ipython_config.py'
115 config_file_name = u'ipython_config.py'
94 # Track the default and actual separately because some messages are
116 #: Track the default and actual separately because some messages are
95 # only printed if we aren't using the default.
117 #: only printed if we aren't using the default.
96 default_config_file_name = config_file_name
118 default_config_file_name = config_file_name
97 default_log_level = logging.WARN
119 default_log_level = logging.WARN
98 # Set by --profile option
120 #: Set by --profile option
99 profile_name = None
121 profile_name = None
100 #: User's ipython directory, typically ~/.ipython/
122 #: User's ipython directory, typically ~/.ipython/
101 ipython_dir = None
123 ipython_dir = None
124 #: internal defaults, implemented in code.
125 default_config = None
126 #: read from the filesystem
127 file_config = None
128 #: read from the system's command line flags
129 command_line_config = None
130 #: passed parametrically to the constructor.
131 constructor_config = None
132 #: final override, if given supercedes file/command/constructor configs
133 override_config = None
102 #: A reference to the argv to be used (typically ends up being sys.argv[1:])
134 #: A reference to the argv to be used (typically ends up being sys.argv[1:])
103 argv = None
135 argv = None
104 #: Default command line arguments. Subclasses should create a new tuple
136 #: Default command line arguments. Subclasses should create a new tuple
105 #: that *includes* these.
137 #: that *includes* these.
106 cl_arguments = app_cl_args
138 cl_arguments = app_cl_args
107
139
140 #: extra arguments computed by the command-line loader
141 extra_args = None
142
108 # Private attributes
143 # Private attributes
109 _exiting = False
144 _exiting = False
110 _initialized = False
145 _initialized = False
@@ -112,8 +147,10 b' class Application(object):'
112 # Class choices for things that will be instantiated at runtime.
147 # Class choices for things that will be instantiated at runtime.
113 _CrashHandler = crashhandler.CrashHandler
148 _CrashHandler = crashhandler.CrashHandler
114
149
115 def __init__(self, argv=None):
150 def __init__(self, argv=None, constructor_config=None, override_config=None):
116 self.argv = sys.argv[1:] if argv is None else argv
151 self.argv = sys.argv[1:] if argv is None else argv
152 self.constructor_config = constructor_config
153 self.override_config = override_config
117 self.init_logger()
154 self.init_logger()
118
155
119 def init_logger(self):
156 def init_logger(self):
@@ -134,7 +171,14 b' class Application(object):'
134 log_level = property(_get_log_level, _set_log_level)
171 log_level = property(_get_log_level, _set_log_level)
135
172
136 def initialize(self):
173 def initialize(self):
137 """Start the application."""
174 """Initialize the application.
175
176 Loads all configuration information and sets all application state, but
177 does not start any relevant processing (typically some kind of event
178 loop).
179
180 Once this method has been called, the application is flagged as
181 initialized and the method becomes a no-op."""
138
182
139 if self._initialized:
183 if self._initialized:
140 return
184 return
@@ -144,31 +188,50 b' class Application(object):'
144 # handler is in place, we can let any subsequent exception propagate,
188 # handler is in place, we can let any subsequent exception propagate,
145 # as our handler will log it with much better detail than the default.
189 # as our handler will log it with much better detail than the default.
146 self.attempt(self.create_crash_handler)
190 self.attempt(self.create_crash_handler)
191
192 # Configuration phase
193 # Default config (internally hardwired in application code)
147 self.create_default_config()
194 self.create_default_config()
148 self.log_default_config()
195 self.log_default_config()
149 self.set_default_config_log_level()
196 self.set_default_config_log_level()
150 self.pre_load_command_line_config()
197
151 self.load_command_line_config()
198 if self.override_config is None:
152 self.set_command_line_config_log_level()
199 # Command-line config
153 self.post_load_command_line_config()
200 self.pre_load_command_line_config()
154 self.log_command_line_config()
201 self.load_command_line_config()
202 self.set_command_line_config_log_level()
203 self.post_load_command_line_config()
204 self.log_command_line_config()
205
206 # Find resources needed for filesystem access, using information from
207 # the above two
155 self.find_ipython_dir()
208 self.find_ipython_dir()
156 self.find_resources()
209 self.find_resources()
157 self.find_config_file_name()
210 self.find_config_file_name()
158 self.find_config_file_paths()
211 self.find_config_file_paths()
159 self.pre_load_file_config()
212
160 self.load_file_config()
213 if self.override_config is None:
161 self.set_file_config_log_level()
214 # File-based config
162 self.post_load_file_config()
215 self.pre_load_file_config()
163 self.log_file_config()
216 self.load_file_config()
217 self.set_file_config_log_level()
218 self.post_load_file_config()
219 self.log_file_config()
220
221 # Merge all config objects into a single one the app can then use
164 self.merge_configs()
222 self.merge_configs()
165 self.log_master_config()
223 self.log_master_config()
224
225 # Construction phase
166 self.pre_construct()
226 self.pre_construct()
167 self.construct()
227 self.construct()
168 self.post_construct()
228 self.post_construct()
229
230 # Done, flag as such and
169 self._initialized = True
231 self._initialized = True
170
232
171 def start(self):
233 def start(self):
234 """Start the application."""
172 self.initialize()
235 self.initialize()
173 self.start_app()
236 self.start_app()
174
237
@@ -356,9 +419,19 b' class Application(object):'
356 """Merge the default, command line and file config objects."""
419 """Merge the default, command line and file config objects."""
357 config = Config()
420 config = Config()
358 config._merge(self.default_config)
421 config._merge(self.default_config)
359 config._merge(self.file_config)
422 if self.override_config is None:
360 config._merge(self.command_line_config)
423 config._merge(self.file_config)
424 config._merge(self.command_line_config)
425 if self.constructor_config is not None:
426 config._merge(self.constructor_config)
427 else:
428 config._merge(self.override_config)
429 # XXX fperez - propose to Brian we rename master_config to simply
430 # config, I think this is going to be heavily used in examples and
431 # application code and the name is shorter/easier to find/remember.
432 # For now, just alias it...
361 self.master_config = config
433 self.master_config = config
434 self.config = config
362
435
363 def log_master_config(self):
436 def log_master_config(self):
364 self.log.debug("Master config created:")
437 self.log.debug("Master config created:")
@@ -361,20 +361,33 b' class IPythonApp(Application):'
361 # Private and configuration attributes
361 # Private and configuration attributes
362 _CrashHandler = crashhandler.IPythonCrashHandler
362 _CrashHandler = crashhandler.IPythonCrashHandler
363
363
364 def __init__(self, argv=None, **shell_params):
364 def __init__(self, argv=None,
365 constructor_config=None, override_config=None,
366 **shell_params):
365 """Create a new IPythonApp.
367 """Create a new IPythonApp.
366
368
369 See the parent class for details on how configuration is handled.
370
367 Parameters
371 Parameters
368 ----------
372 ----------
369 argv : optional, list
373 argv : optional, list
370 If given, used as the command-line argv environment to read arguments
374 If given, used as the command-line argv environment to read arguments
371 from.
375 from.
372
376
377 constructor_config : optional, Config
378 If given, additional config that is merged last, after internal
379 defaults, command-line and file-based configs.
380
381 override_config : optional, Config
382 If given, config that overrides all others unconditionally (except
383 for internal defaults, which ensure that all parameters exist).
384
373 shell_params : optional, dict
385 shell_params : optional, dict
374 All other keywords are passed to the :class:`iplib.InteractiveShell`
386 All other keywords are passed to the :class:`iplib.InteractiveShell`
375 constructor.
387 constructor.
376 """
388 """
377 super(IPythonApp, self).__init__(argv)
389 super(IPythonApp, self).__init__(argv, constructor_config,
390 override_config)
378 self.shell_params = shell_params
391 self.shell_params = shell_params
379
392
380 def create_default_config(self):
393 def create_default_config(self):
@@ -449,8 +462,7 b' class IPythonApp(Application):'
449 # unless the -i flag (Global.force_interact) is true.
462 # unless the -i flag (Global.force_interact) is true.
450 code_to_run = config.Global.get('code_to_run','')
463 code_to_run = config.Global.get('code_to_run','')
451 file_to_run = False
464 file_to_run = False
452 if len(self.extra_args)>=1:
465 if self.extra_args and self.extra_args[0]:
453 if self.extra_args[0]:
454 file_to_run = True
466 file_to_run = True
455 if file_to_run or code_to_run:
467 if file_to_run or code_to_run:
456 if not config.Global.force_interact:
468 if not config.Global.force_interact:
General Comments 0
You need to be logged in to leave comments. Login now