##// END OF EJS Templates
Merge pull request #4048 from minrk/finish-notes...
Min RK -
r12517:829790a6 merge
parent child Browse files
Show More
@@ -1,16 +1,14 b''
1 1 # http://travis-ci.org/#!/ipython/ipython
2 2 language: python
3 3 python:
4 4 - 2.7
5 5 - 3.3
6 6 before_install:
7 - pip install jinja2
8 7 - easy_install -q pyzmq
8 - pip install jinja2 sphinx pygments tornado
9 9 - sudo apt-get install pandoc
10 - pip install pygments
11 - pip install sphinx
12 10 install:
13 11 - python setup.py install -q
14 12 script:
15 13 - if [[ $TRAVIS_PYTHON_VERSION == '2.'* ]]; then iptest -w /tmp; fi
16 14 - if [[ $TRAVIS_PYTHON_VERSION == '3.'* ]]; then iptest3 -w /tmp; fi
@@ -1,324 +1,323 b''
1 1 #!/usr/bin/env python
2 2 """NBConvert is a utility for conversion of .ipynb files.
3 3
4 4 Command-line interface for the NbConvert conversion utility.
5 5 """
6 6 #-----------------------------------------------------------------------------
7 7 #Copyright (c) 2013, the IPython Development Team.
8 8 #
9 9 #Distributed under the terms of the Modified BSD License.
10 10 #
11 11 #The full license is in the file COPYING.txt, distributed with this software.
12 12 #-----------------------------------------------------------------------------
13 13
14 14 #-----------------------------------------------------------------------------
15 15 #Imports
16 16 #-----------------------------------------------------------------------------
17 17
18 18 # Stdlib imports
19 19 from __future__ import print_function
20 20
21 21 import logging
22 22 import sys
23 23 import os
24 24 import glob
25 25
26 26 # From IPython
27 27 from IPython.core.application import BaseIPythonApplication, base_aliases, base_flags
28 28 from IPython.core.profiledir import ProfileDir
29 29 from IPython.config import catch_config_error, Configurable
30 30 from IPython.utils.traitlets import (
31 31 Unicode, List, Instance, DottedObjectName, Type, CaselessStrEnum,
32 32 )
33 33 from IPython.utils.importstring import import_item
34 34 from IPython.utils.text import dedent
35 35
36 36 from .exporters.export import get_export_names, exporter_map
37 37 from IPython.nbconvert import exporters, preprocessors, writers, postprocessors
38 38 from .utils.base import NbConvertBase
39 39 from .utils.exceptions import ConversionException
40 40
41 41 #-----------------------------------------------------------------------------
42 42 #Classes and functions
43 43 #-----------------------------------------------------------------------------
44 44
45 45 class DottedOrNone(DottedObjectName):
46 46 """
47 47 A string holding a valid dotted object name in Python, such as A.b3._c
48 48 Also allows for None type."""
49 49
50 50 default_value = u''
51 51
52 52 def validate(self, obj, value):
53 53 if value is not None and len(value) > 0:
54 54 return super(DottedOrNone, self).validate(obj, value)
55 55 else:
56 56 return value
57 57
58 58 nbconvert_aliases = {}
59 59 nbconvert_aliases.update(base_aliases)
60 60 nbconvert_aliases.update({
61 61 'to' : 'NbConvertApp.export_format',
62 62 'template' : 'Exporter.template_file',
63 63 'writer' : 'NbConvertApp.writer_class',
64 64 'post': 'NbConvertApp.postprocessor_class',
65 65 'output': 'NbConvertApp.output_base',
66 'offline-slides': 'RevealHelpPreprocessor.url_prefix',
67 'slide-notes': 'RevealHelpPreprocessor.speaker_notes'
66 'reveal-prefix': 'RevealHelpPreprocessor.url_prefix',
68 67 })
69 68
70 69 nbconvert_flags = {}
71 70 nbconvert_flags.update(base_flags)
72 71 nbconvert_flags.update({
73 72 'stdout' : (
74 73 {'NbConvertApp' : {'writer_class' : "StdoutWriter"}},
75 74 "Write notebook output to stdout instead of files."
76 75 )
77 76 })
78 77
79 78
80 79 class NbConvertApp(BaseIPythonApplication):
81 80 """Application used to convert to and from notebook file type (*.ipynb)"""
82 81
83 82 name = 'ipython-nbconvert'
84 83 aliases = nbconvert_aliases
85 84 flags = nbconvert_flags
86 85
87 86 def _log_level_default(self):
88 87 return logging.INFO
89 88
90 89 def _classes_default(self):
91 90 classes = [NbConvertBase, ProfileDir]
92 91 for pkg in (exporters, preprocessors, writers):
93 92 for name in dir(pkg):
94 93 cls = getattr(pkg, name)
95 94 if isinstance(cls, type) and issubclass(cls, Configurable):
96 95 classes.append(cls)
97 96
98 97 return classes
99 98
100 99 description = Unicode(
101 100 u"""This application is used to convert notebook files (*.ipynb)
102 101 to various other formats.
103 102
104 103 WARNING: THE COMMANDLINE INTERFACE MAY CHANGE IN FUTURE RELEASES.""")
105 104
106 105 output_base = Unicode('', config=True, help='''overwrite base name use for output files.
107 106 can only be use when converting one notebook at a time.
108 107 ''')
109 108
110 109 examples = Unicode(u"""
111 110 The simplest way to use nbconvert is
112 111
113 112 > ipython nbconvert mynotebook.ipynb
114 113
115 114 which will convert mynotebook.ipynb to the default format (probably HTML).
116 115
117 116 You can specify the export format with `--to`.
118 117 Options include {0}
119 118
120 119 > ipython nbconvert --to latex mynotebook.ipnynb
121 120
122 121 Both HTML and LaTeX support multiple output templates. LaTeX includes
123 122 'basic', 'book', and 'article'. HTML includes 'basic' and 'full'. You
124 123 can specify the flavor of the format used.
125 124
126 125 > ipython nbconvert --to html --template basic mynotebook.ipynb
127 126
128 127 You can also pipe the output to stdout, rather than a file
129 128
130 129 > ipython nbconvert mynotebook.ipynb --stdout
131 130
132 131 A post-processor can be used to compile a PDF
133 132
134 133 > ipython nbconvert mynotebook.ipynb --to latex --post PDF
135 134
136 135 You can get (and serve) a Reveal.js-powered slideshow
137 136
138 137 > ipython nbconvert myslides.ipynb --to slides --post serve
139 138
140 139 Multiple notebooks can be given at the command line in a couple of
141 140 different ways:
142 141
143 142 > ipython nbconvert notebook*.ipynb
144 143 > ipython nbconvert notebook1.ipynb notebook2.ipynb
145 144
146 145 or you can specify the notebooks list in a config file, containing::
147 146
148 147 c.NbConvertApp.notebooks = ["my_notebook.ipynb"]
149 148
150 149 > ipython nbconvert --config mycfg.py
151 150 """.format(get_export_names()))
152 151
153 152 # Writer specific variables
154 153 writer = Instance('IPython.nbconvert.writers.base.WriterBase',
155 154 help="""Instance of the writer class used to write the
156 155 results of the conversion.""")
157 156 writer_class = DottedObjectName('FilesWriter', config=True,
158 157 help="""Writer class used to write the
159 158 results of the conversion""")
160 159 writer_aliases = {'fileswriter': 'IPython.nbconvert.writers.files.FilesWriter',
161 160 'debugwriter': 'IPython.nbconvert.writers.debug.DebugWriter',
162 161 'stdoutwriter': 'IPython.nbconvert.writers.stdout.StdoutWriter'}
163 162 writer_factory = Type()
164 163
165 164 def _writer_class_changed(self, name, old, new):
166 165 if new.lower() in self.writer_aliases:
167 166 new = self.writer_aliases[new.lower()]
168 167 self.writer_factory = import_item(new)
169 168
170 169 # Post-processor specific variables
171 170 postprocessor = Instance('IPython.nbconvert.postprocessors.base.PostProcessorBase',
172 171 help="""Instance of the PostProcessor class used to write the
173 172 results of the conversion.""")
174 173
175 174 postprocessor_class = DottedOrNone(config=True,
176 175 help="""PostProcessor class used to write the
177 176 results of the conversion""")
178 177 postprocessor_aliases = {'pdf': 'IPython.nbconvert.postprocessors.pdf.PDFPostProcessor',
179 178 'serve': 'IPython.nbconvert.postprocessors.serve.ServePostProcessor'}
180 179 postprocessor_factory = Type()
181 180
182 181 def _postprocessor_class_changed(self, name, old, new):
183 182 if new.lower() in self.postprocessor_aliases:
184 183 new = self.postprocessor_aliases[new.lower()]
185 184 if new:
186 185 self.postprocessor_factory = import_item(new)
187 186
188 187
189 188 # Other configurable variables
190 189 export_format = CaselessStrEnum(get_export_names(),
191 190 default_value="html",
192 191 config=True,
193 192 help="""The export format to be used."""
194 193 )
195 194
196 195 notebooks = List([], config=True, help="""List of notebooks to convert.
197 196 Wildcards are supported.
198 197 Filenames passed positionally will be added to the list.
199 198 """)
200 199
201 200 @catch_config_error
202 201 def initialize(self, argv=None):
203 202 super(NbConvertApp, self).initialize(argv)
204 203 self.init_syspath()
205 204 self.init_notebooks()
206 205 self.init_writer()
207 206 self.init_postprocessor()
208 207
209 208
210 209
211 210 def init_syspath(self):
212 211 """
213 212 Add the cwd to the sys.path ($PYTHONPATH)
214 213 """
215 214 sys.path.insert(0, os.getcwd())
216 215
217 216
218 217 def init_notebooks(self):
219 218 """Construct the list of notebooks.
220 219 If notebooks are passed on the command-line,
221 220 they override notebooks specified in config files.
222 221 Glob each notebook to replace notebook patterns with filenames.
223 222 """
224 223
225 224 # Specifying notebooks on the command-line overrides (rather than adds)
226 225 # the notebook list
227 226 if self.extra_args:
228 227 patterns = self.extra_args
229 228 else:
230 229 patterns = self.notebooks
231 230
232 231 # Use glob to replace all the notebook patterns with filenames.
233 232 filenames = []
234 233 for pattern in patterns:
235 234
236 235 # Use glob to find matching filenames. Allow the user to convert
237 236 # notebooks without having to type the extension.
238 237 globbed_files = glob.glob(pattern)
239 238 globbed_files.extend(glob.glob(pattern + '.ipynb'))
240 239 if not globbed_files:
241 240 self.log.warn("pattern %r matched no files", pattern)
242 241
243 242 for filename in globbed_files:
244 243 if not filename in filenames:
245 244 filenames.append(filename)
246 245 self.notebooks = filenames
247 246
248 247 def init_writer(self):
249 248 """
250 249 Initialize the writer (which is stateless)
251 250 """
252 251 self._writer_class_changed(None, self.writer_class, self.writer_class)
253 252 self.writer = self.writer_factory(parent=self)
254 253
255 254 def init_postprocessor(self):
256 255 """
257 256 Initialize the postprocessor (which is stateless)
258 257 """
259 258 self._postprocessor_class_changed(None, self.postprocessor_class,
260 259 self.postprocessor_class)
261 260 if self.postprocessor_factory:
262 261 self.postprocessor = self.postprocessor_factory(parent=self)
263 262
264 263 def start(self):
265 264 """
266 265 Ran after initialization completed
267 266 """
268 267 super(NbConvertApp, self).start()
269 268 self.convert_notebooks()
270 269
271 270 def convert_notebooks(self):
272 271 """
273 272 Convert the notebooks in the self.notebook traitlet
274 273 """
275 274 # Export each notebook
276 275 conversion_success = 0
277 276
278 277 if self.output_base != '' and len(self.notebooks) > 1:
279 278 self.log.error(
280 279 """UsageError: --output flag or `NbConvertApp.output_base` config option
281 280 cannot be used when converting multiple notebooks.
282 281 """)
283 282 self.exit(1)
284 283
285 284 exporter = exporter_map[self.export_format](config=self.config)
286 285
287 286 for notebook_filename in self.notebooks:
288 287 self.log.info("Converting notebook %s to %s", notebook_filename, self.export_format)
289 288
290 289 # Get a unique key for the notebook and set it in the resources object.
291 290 basename = os.path.basename(notebook_filename)
292 291 notebook_name = basename[:basename.rfind('.')]
293 292 if self.output_base:
294 293 notebook_name = self.output_base
295 294 resources = {}
296 295 resources['unique_key'] = notebook_name
297 296 resources['output_files_dir'] = '%s_files' % notebook_name
298 297 self.log.info("Support files will be in %s", os.path.join(resources['output_files_dir'], ''))
299 298
300 299 # Try to export
301 300 try:
302 301 output, resources = exporter.from_filename(notebook_filename, resources=resources)
303 302 except ConversionException as e:
304 303 self.log.error("Error while converting '%s'", notebook_filename,
305 304 exc_info=True)
306 305 self.exit(1)
307 306 else:
308 307 write_resultes = self.writer.write(output, resources, notebook_name=notebook_name)
309 308
310 309 #Post-process if post processor has been defined.
311 310 if hasattr(self, 'postprocessor') and self.postprocessor:
312 311 self.postprocessor(write_resultes)
313 312 conversion_success += 1
314 313
315 314 # If nothing was converted successfully, help the user.
316 315 if conversion_success == 0:
317 316 self.print_help()
318 317 sys.exit(-1)
319 318
320 319 #-----------------------------------------------------------------------------
321 320 # Main entry point
322 321 #-----------------------------------------------------------------------------
323 322
324 323 launch_new_instance = NbConvertApp.launch_instance
@@ -1,3 +1,8 b''
1 1 from .base import PostProcessorBase
2 2 from .pdf import PDFPostProcessor
3 from .serve import ServePostProcessor
3
4 # protect against unavailable tornado
5 try:
6 from .serve import ServePostProcessor
7 except ImportError:
8 pass
@@ -1,55 +1,107 b''
1 """
2 Contains postprocessor for serving nbconvert output.
3 """
1 """PostProcessor for serving reveal.js HTML slideshows."""
4 2 #-----------------------------------------------------------------------------
5 3 #Copyright (c) 2013, the IPython Development Team.
6 4 #
7 5 #Distributed under the terms of the Modified BSD License.
8 6 #
9 7 #The full license is in the file COPYING.txt, distributed with this software.
10 8 #-----------------------------------------------------------------------------
11 9
12 10 #-----------------------------------------------------------------------------
13 11 # Imports
14 12 #-----------------------------------------------------------------------------
15 13
16 14 import os
17 15 import webbrowser
18 16
19 from BaseHTTPServer import HTTPServer
20 from SimpleHTTPServer import SimpleHTTPRequestHandler
17 from tornado import web, ioloop, httpserver
18 from tornado.httpclient import AsyncHTTPClient
21 19
22 from IPython.utils.traitlets import Bool
20 from IPython.utils.traitlets import Bool, Unicode, Int
23 21
24 22 from .base import PostProcessorBase
25 23
26 24 #-----------------------------------------------------------------------------
27 25 # Classes
28 26 #-----------------------------------------------------------------------------
27
28 class ProxyHandler(web.RequestHandler):
29 """handler the proxies requests from a local prefix to a CDN"""
30 @web.asynchronous
31 def get(self, prefix, url):
32 """proxy a request to a CDN"""
33 proxy_url = "/".join([self.settings['cdn'], url])
34 client = self.settings['client']
35 client.fetch(proxy_url, callback=self.finish_get)
36
37 def finish_get(self, response):
38 """finish the request"""
39 # copy potentially relevant headers
40 for header in ["Content-Type", "Cache-Control", "Date", "Last-Modified", "Expires"]:
41 if header in response.headers:
42 self.set_header(header, response.headers[header])
43 self.finish(response.body)
44
29 45 class ServePostProcessor(PostProcessorBase):
30 """Post processor designed to serve files"""
46 """Post processor designed to serve files
47
48 Proxies reveal.js requests to a CDN if no local reveal.js is present
49 """
31 50
32 51
33 52 open_in_browser = Bool(True, config=True,
34 help="""Set to False to deactivate
35 the opening of the browser""")
53 help="""Should the browser be opened automatically?"""
54 )
55 reveal_cdn = Unicode("https://cdn.jsdelivr.net/reveal.js/2.4.0", config=True,
56 help="""URL for reveal.js CDN."""
57 )
58 reveal_prefix = Unicode("reveal.js", config=True, help="URL prefix for reveal.js")
59 ip = Unicode("127.0.0.1", config=True, help="The IP address to listen on.")
60 port = Int(8000, config=True, help="port for the server to listen on.")
36 61
37 62 def postprocess(self, input):
38 """
39 Simple implementation to serve the build directory.
40 """
41
63 """Serve the build directory with a webserver."""
64 dirname, filename = os.path.split(input)
65 handlers = [
66 (r"/(.+)", web.StaticFileHandler, {'path' : dirname}),
67 (r"/", web.RedirectHandler, {"url": "/%s" % filename})
68 ]
69
70 if ('://' in self.reveal_prefix or self.reveal_prefix.startswith("//")):
71 # reveal specifically from CDN, nothing to do
72 pass
73 elif os.path.isdir(os.path.join(dirname, self.reveal_prefix)):
74 # reveal prefix exists
75 self.log.info("Serving local %s", self.reveal_prefix)
76 else:
77 self.log.info("Redirecting %s requests to %s", self.reveal_prefix, self.reveal_cdn)
78 handlers.insert(0, (r"/(%s)/(.*)" % self.reveal_prefix, ProxyHandler))
79
80 app = web.Application(handlers,
81 cdn=self.reveal_cdn,
82 client=AsyncHTTPClient(),
83 )
84 # hook up tornado logging to our logger
85 from tornado import log
86 log.app_log = self.log
87
88 http_server = httpserver.HTTPServer(app)
89 http_server.listen(self.port, address=self.ip)
90 url = "http://%s:%i/%s" % (self.ip, self.port, filename)
91 print("Serving your slides at %s" % url)
92 print("Use Control-C to stop this server")
93 if self.open_in_browser:
94 webbrowser.open(url, new=2)
42 95 try:
43 dirname, filename = os.path.split(input)
44 if dirname:
45 os.chdir(dirname)
46 httpd = HTTPServer(('127.0.0.1', 8000), SimpleHTTPRequestHandler)
47 sa = httpd.socket.getsockname()
48 url = "http://" + sa[0] + ":" + str(sa[1]) + "/" + filename
49 if self.open_in_browser:
50 webbrowser.open(url, new=2)
51 print("Serving your slides on " + url)
52 print("Use Control-C to stop this server.")
53 httpd.serve_forever()
96 ioloop.IOLoop.instance().start()
54 97 except KeyboardInterrupt:
55 print("The server is shut down.")
98 print("\nInterrupted")
99
100 def main(path):
101 """allow running this module to serve the slides"""
102 server = ServePostProcessor()
103 server(path)
104
105 if __name__ == '__main__':
106 import sys
107 main(sys.argv[1])
@@ -1,94 +1,67 b''
1 1 """Module that pre-processes the notebook for export via Reveal.
2 2 """
3 3 #-----------------------------------------------------------------------------
4 4 # Copyright (c) 2013, the IPython Development Team.
5 5 #
6 6 # Distributed under the terms of the Modified BSD License.
7 7 #
8 8 # The full license is in the file COPYING.txt, distributed with this software.
9 9 #-----------------------------------------------------------------------------
10 10
11 11 #-----------------------------------------------------------------------------
12 12 # Imports
13 13 #-----------------------------------------------------------------------------
14 14
15 15 import os
16 16 import urllib2
17 17
18 18 from .base import Preprocessor
19 19 from IPython.utils.traitlets import Unicode, Bool
20 20
21 21 #-----------------------------------------------------------------------------
22 22 # Classes and functions
23 23 #-----------------------------------------------------------------------------
24 24
25 25 class RevealHelpPreprocessor(Preprocessor):
26 26
27 url_prefix = Unicode('//cdn.jsdelivr.net/reveal.js/2.4.0',
28 config=True,
29 help="""If you want to use a local reveal.js library,
30 use 'url_prefix':'reveal.js' in your config object.""")
31
32 speaker_notes = Bool(False,
33 config=True,
34 help="""If you want to use the speaker notes
35 set this to True.""")
27 url_prefix = Unicode('reveal.js', config=True,
28 help="""The URL prefix for reveal.js.
29 This can be a a relative URL for a local copy of reveal.js,
30 or point to a CDN.
31
32 For speaker notes to work, a local reveal.js prefix must be used.
33 """
34 )
36 35
37 36 def preprocess(self, nb, resources):
38 37 """
39 38 Called once to 'preprocess' contents of the notebook.
40 39
41 40 Parameters
42 41 ----------
43 42 nb : NotebookNode
44 43 Notebook being converted
45 44 resources : dictionary
46 45 Additional resources used in the conversion process. Allows
47 46 preprocessors to pass variables into the Jinja engine.
48 47 """
49 48
50 49 for worksheet in nb.worksheets :
51 50 for index, cell in enumerate(worksheet.cells):
52 51
53 52 #Make sure the cell has slideshow metadata.
54 53 cell.metadata.align_type = cell.get('metadata', {}).get('slideshow', {}).get('align_type', 'Left')
55 54 cell.metadata.slide_type = cell.get('metadata', {}).get('slideshow', {}).get('slide_type', '-')
56 55
57 56 #Get the slide type. If type is start of subslide or slide,
58 57 #end the last subslide/slide.
59 58 if cell.metadata.slide_type in ['slide']:
60 59 worksheet.cells[index - 1].metadata.slide_helper = 'slide_end'
61 60 if cell.metadata.slide_type in ['subslide']:
62 61 worksheet.cells[index - 1].metadata.slide_helper = 'subslide_end'
63 62
64 63
65 64 if not isinstance(resources['reveal'], dict):
66 65 resources['reveal'] = {}
67 66 resources['reveal']['url_prefix'] = self.url_prefix
68 resources['reveal']['notes_prefix'] = self.url_prefix
69
70 cdn = 'http://cdn.jsdelivr.net/reveal.js/2.4.0'
71 local = 'local'
72 html_path = 'plugin/notes/notes.html'
73 js_path = 'plugin/notes/notes.js'
74
75 html_infile = os.path.join(cdn, html_path)
76 js_infile = os.path.join(cdn, js_path)
77 html_outfile = os.path.join(local, html_path)
78 js_outfile = os.path.join(local, js_path)
79
80 if self.speaker_notes:
81 if 'outputs' not in resources:
82 resources['outputs'] = {}
83 resources['outputs'][html_outfile] = self.notes_helper(html_infile)
84 resources['outputs'][js_outfile] = self.notes_helper(js_infile)
85 resources['reveal']['notes_prefix'] = local
86
87 67 return nb, resources
88
89 def notes_helper(self, infile):
90 """Helper function to get the content from an url."""
91
92 content = urllib2.urlopen(infile).read()
93
94 return content
@@ -1,185 +1,185 b''
1 1 {%- extends 'reveal_internals/slides.tpl' -%}
2 2
3 3
4 4 {% block header %}
5 5 <!DOCTYPE html>
6 6 <html>
7 7 <head>
8 8
9 9 <meta charset="utf-8" />
10 10 <meta http-equiv="X-UA-Compatible" content="chrome=1">
11 11
12 12 <meta name="apple-mobile-web-app-capable" content="yes" />
13 13 <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
14 14
15 15 <!-- General and theme style sheets -->
16 16 <link rel="stylesheet" href="{{resources.reveal.url_prefix}}/css/reveal.css">
17 17 <link rel="stylesheet" href="{{resources.reveal.url_prefix}}/css/theme/simple.css" id="theme">
18 18
19 19 <!-- For syntax highlighting -->
20 20 <link rel="stylesheet" href="{{resources.reveal.url_prefix}}/lib/css/zenburn.css">
21 21
22 22 <!-- If the query includes 'print-pdf', use the PDF print sheet -->
23 23 <script>
24 24 document.write( '<link rel="stylesheet" href="{{resources.reveal.url_prefix}}/css/print/' + ( window.location.search.match( /print-pdf/gi ) ? 'pdf' : 'paper' ) + '.css" type="text/css" media="print">' );
25 25 </script>
26 26
27 27 <!--[if lt IE 9]>
28 28 <script src="{{resources.reveal.url_prefix}}/lib/js/html5shiv.js"></script>
29 29 <![endif]-->
30 30
31 31 {% for css in resources.inlining.css -%}
32 32 <style type="text/css">
33 33 {{ css }}
34 34 </style>
35 35 {% endfor %}
36 36
37 37 <style type="text/css">
38 38 /* Overrides of notebook CSS for static HTML export */
39 39 .reveal {
40 40 font-size: 20px;
41 41 overflow-y: auto;
42 42 overflow-x: hidden;
43 43 }
44 44 .reveal pre {
45 45 width: 95%;
46 46 padding: 0.4em;
47 47 margin: 0px;
48 48 font-family: monospace, sans-serif;
49 49 font-size: 80%;
50 50 box-shadow: 0px 0px 0px rgba(0, 0, 0, 0);
51 51 }
52 52 .reveal section img {
53 53 border: 0px solid black;
54 54 box-shadow: 0 0 10px rgba(0, 0, 0, 0);
55 55 }
56 56 .reveal .slides {
57 57 text-align: left;
58 58 }
59 59 .reveal.fade {
60 60 opacity: 1;
61 61 }
62 62 div.input_area {
63 63 padding: 0.06em;
64 64 }
65 65 div.code_cell {
66 66 background-color: transparent;
67 67 }
68 68 div.prompt {
69 69 width: 11ex;
70 70 padding: 0.4em;
71 71 margin: 0px;
72 72 font-family: monospace, sans-serif;
73 73 font-size: 80%;
74 74 text-align: right;
75 75 }
76 76 div.output_area pre {
77 77 font-family: monospace, sans-serif;
78 78 font-size: 80%;
79 79 }
80 80 div.output_prompt {
81 81 /* 5px right shift to account for margin in parent container */
82 82 margin: 5px 5px 0 0;
83 83 }
84 84 .rendered_html p {
85 85 text-align: inherit;
86 86 }
87 87 </style>
88 88
89 89 <!-- Custom stylesheet, it must be in the same directory as the html file -->
90 90 <link rel="stylesheet" href="custom.css">
91 91
92 92 </head>
93 93 {% endblock header%}
94 94
95 95
96 96 {% block body %}
97 97 <body>
98 98 <div class="reveal">
99 99 <div class="slides">
100 100 {{ super() }}
101 101 </div>
102 102 </div>
103 103
104 104 <!--
105 105 Uncomment the following block and the addthis_widget.js (see below inside dependencies)
106 106 to get enable social buttons.
107 107 -->
108 108
109 109 <!--
110 110 <div class="addthis_toolbox addthis_floating_style addthis_32x32_style" style="left:20px;top:20px;">
111 111 <a class="addthis_button_twitter"></a>
112 112 <a class="addthis_button_google_plusone_share"></a>
113 113 <a class="addthis_button_linkedin"></a>
114 114 <a class="addthis_button_facebook"></a>
115 115 <a class="addthis_button_more"></a>
116 116 </div>
117 117 -->
118 118
119 119 <script src="{{resources.reveal.url_prefix}}/lib/js/head.min.js"></script>
120 120
121 121 <script src="{{resources.reveal.url_prefix}}/js/reveal.js"></script>
122 122
123 123 <script>
124 124
125 125 // Full list of configuration options available here: https://github.com/hakimel/reveal.js#configuration
126 126 Reveal.initialize({
127 127 controls: true,
128 128 progress: true,
129 129 history: true,
130 130
131 131 theme: Reveal.getQueryHash().theme, // available themes are in /css/theme
132 132 transition: Reveal.getQueryHash().transition || 'linear', // default/cube/page/concave/zoom/linear/none
133 133
134 134 // Optional libraries used to extend on reveal.js
135 135 dependencies: [
136 136 { src: "{{resources.reveal.url_prefix}}/lib/js/classList.js", condition: function() { return !document.body.classList; } },
137 137 { src: "{{resources.reveal.url_prefix}}/plugin/highlight/highlight.js", async: true, callback: function() { hljs.initHighlightingOnLoad(); } },
138 { src: "{{resources.reveal.notes_prefix}}/plugin/notes/notes.js", async: true, condition: function() { return !!document.body.classList; } }
138 { src: "{{resources.reveal.url_prefix}}/plugin/notes/notes.js", async: true, condition: function() { return !!document.body.classList; } }
139 139 // { src: 'http://s7.addthis.com/js/300/addthis_widget.js', async: true},
140 140 ]
141 141 });
142 142 </script>
143 143
144 144 <!-- MathJax configuration -->
145 145 <script type="text/x-mathjax-config">
146 146 MathJax.Hub.Config({
147 147 tex2jax: {
148 148 inlineMath: [ ['$','$'], ["\\(","\\)"] ],
149 149 displayMath: [ ['$$','$$'], ["\\[","\\]"] ]
150 150 },
151 151 displayAlign: 'left', // Change this to 'center' to center equations.
152 152 "HTML-CSS": {
153 153 styles: {'.MathJax_Display': {"margin": 0}}
154 154 }
155 155 });
156 156 </script>
157 157 <!-- End of mathjax configuration -->
158 158
159 159 <script>
160 160 // We wait for the onload function to load MathJax after the page is completely loaded.
161 161 // MathJax is loaded 1 unit of time after the page is ready.
162 162 // This hack prevent problems when you load multiple js files (i.e. social button from addthis).
163 163 //
164 164 window.onload = function () {
165 165 setTimeout(function () {
166 166 var script = document.createElement("script");
167 167 script.type = "text/javascript";
168 168 script.src = "https://c328740.ssl.cf1.rackcdn.com/mathjax/latest/MathJax.js?config=TeX-AMS_HTML";
169 169 document.getElementsByTagName("head")[0].appendChild(script);
170 170 },1)
171 171 }
172 172 </script>
173 173
174 174 <script>
175 175 Reveal.addEventListener( 'slidechanged', function( event ) {
176 176 MathJax.Hub.Rerender(event.currentSlide);
177 177 });
178 178 </script>
179 179
180 180 </body>
181 181 {% endblock body %}
182 182
183 183 {% block footer %}
184 184 </html>
185 185 {% endblock footer %} No newline at end of file
@@ -1,654 +1,656 b''
1 1 # -*- coding: utf-8 -*-
2 2 """IPython Test Suite Runner.
3 3
4 4 This module provides a main entry point to a user script to test IPython
5 5 itself from the command line. There are two ways of running this script:
6 6
7 7 1. With the syntax `iptest all`. This runs our entire test suite by
8 8 calling this script (with different arguments) recursively. This
9 9 causes modules and package to be tested in different processes, using nose
10 10 or trial where appropriate.
11 11 2. With the regular nose syntax, like `iptest -vvs IPython`. In this form
12 12 the script simply calls nose, but with special command line flags and
13 13 plugins loaded.
14 14
15 15 """
16 16
17 17 #-----------------------------------------------------------------------------
18 18 # Copyright (C) 2009-2011 The IPython Development Team
19 19 #
20 20 # Distributed under the terms of the BSD License. The full license is in
21 21 # the file COPYING, distributed as part of this software.
22 22 #-----------------------------------------------------------------------------
23 23
24 24 #-----------------------------------------------------------------------------
25 25 # Imports
26 26 #-----------------------------------------------------------------------------
27 27 from __future__ import print_function
28 28
29 29 # Stdlib
30 30 import glob
31 31 import os
32 32 import os.path as path
33 33 import signal
34 34 import sys
35 35 import subprocess
36 36 import tempfile
37 37 import time
38 38 import warnings
39 39 import multiprocessing.pool
40 40
41 41 # Now, proceed to import nose itself
42 42 import nose.plugins.builtin
43 43 from nose.plugins.xunit import Xunit
44 44 from nose import SkipTest
45 45 from nose.core import TestProgram
46 46
47 47 # Our own imports
48 48 from IPython.utils import py3compat
49 49 from IPython.utils.importstring import import_item
50 50 from IPython.utils.path import get_ipython_module_path, get_ipython_package_dir
51 51 from IPython.utils.process import pycmd2argv
52 52 from IPython.utils.sysinfo import sys_info
53 53 from IPython.utils.tempdir import TemporaryDirectory
54 54 from IPython.utils.warn import warn
55 55
56 56 from IPython.testing import globalipapp
57 57 from IPython.testing.plugin.ipdoctest import IPythonDoctest
58 58 from IPython.external.decorators import KnownFailure, knownfailureif
59 59
60 60 pjoin = path.join
61 61
62 62
63 63 #-----------------------------------------------------------------------------
64 64 # Globals
65 65 #-----------------------------------------------------------------------------
66 66
67 67
68 68 #-----------------------------------------------------------------------------
69 69 # Warnings control
70 70 #-----------------------------------------------------------------------------
71 71
72 72 # Twisted generates annoying warnings with Python 2.6, as will do other code
73 73 # that imports 'sets' as of today
74 74 warnings.filterwarnings('ignore', 'the sets module is deprecated',
75 75 DeprecationWarning )
76 76
77 77 # This one also comes from Twisted
78 78 warnings.filterwarnings('ignore', 'the sha module is deprecated',
79 79 DeprecationWarning)
80 80
81 81 # Wx on Fedora11 spits these out
82 82 warnings.filterwarnings('ignore', 'wxPython/wxWidgets release number mismatch',
83 83 UserWarning)
84 84
85 85 # ------------------------------------------------------------------------------
86 86 # Monkeypatch Xunit to count known failures as skipped.
87 87 # ------------------------------------------------------------------------------
88 88 def monkeypatch_xunit():
89 89 try:
90 90 knownfailureif(True)(lambda: None)()
91 91 except Exception as e:
92 92 KnownFailureTest = type(e)
93 93
94 94 def addError(self, test, err, capt=None):
95 95 if issubclass(err[0], KnownFailureTest):
96 96 err = (SkipTest,) + err[1:]
97 97 return self.orig_addError(test, err, capt)
98 98
99 99 Xunit.orig_addError = Xunit.addError
100 100 Xunit.addError = addError
101 101
102 102 #-----------------------------------------------------------------------------
103 103 # Logic for skipping doctests
104 104 #-----------------------------------------------------------------------------
105 105 def extract_version(mod):
106 106 return mod.__version__
107 107
108 108 def test_for(item, min_version=None, callback=extract_version):
109 109 """Test to see if item is importable, and optionally check against a minimum
110 110 version.
111 111
112 112 If min_version is given, the default behavior is to check against the
113 113 `__version__` attribute of the item, but specifying `callback` allows you to
114 114 extract the value you are interested in. e.g::
115 115
116 116 In [1]: import sys
117 117
118 118 In [2]: from IPython.testing.iptest import test_for
119 119
120 120 In [3]: test_for('sys', (2,6), callback=lambda sys: sys.version_info)
121 121 Out[3]: True
122 122
123 123 """
124 124 try:
125 125 check = import_item(item)
126 126 except (ImportError, RuntimeError):
127 127 # GTK reports Runtime error if it can't be initialized even if it's
128 128 # importable.
129 129 return False
130 130 else:
131 131 if min_version:
132 132 if callback:
133 133 # extra processing step to get version to compare
134 134 check = callback(check)
135 135
136 136 return check >= min_version
137 137 else:
138 138 return True
139 139
140 140 # Global dict where we can store information on what we have and what we don't
141 141 # have available at test run time
142 142 have = {}
143 143
144 144 have['curses'] = test_for('_curses')
145 145 have['matplotlib'] = test_for('matplotlib')
146 146 have['numpy'] = test_for('numpy')
147 147 have['pexpect'] = test_for('IPython.external.pexpect')
148 148 have['pymongo'] = test_for('pymongo')
149 149 have['pygments'] = test_for('pygments')
150 150 have['qt'] = test_for('IPython.external.qt')
151 151 have['rpy2'] = test_for('rpy2')
152 152 have['sqlite3'] = test_for('sqlite3')
153 153 have['cython'] = test_for('Cython')
154 154 have['oct2py'] = test_for('oct2py')
155 155 have['tornado'] = test_for('tornado.version_info', (2,1,0), callback=None)
156 156 have['jinja2'] = test_for('jinja2')
157 157 have['wx'] = test_for('wx')
158 158 have['wx.aui'] = test_for('wx.aui')
159 159 have['azure'] = test_for('azure')
160 160 have['sphinx'] = test_for('sphinx')
161 161
162 162 min_zmq = (2,1,11)
163 163
164 164 have['zmq'] = test_for('zmq.pyzmq_version_info', min_zmq, callback=lambda x: x())
165 165
166 166 #-----------------------------------------------------------------------------
167 167 # Functions and classes
168 168 #-----------------------------------------------------------------------------
169 169
170 170 def report():
171 171 """Return a string with a summary report of test-related variables."""
172 172
173 173 out = [ sys_info(), '\n']
174 174
175 175 avail = []
176 176 not_avail = []
177 177
178 178 for k, is_avail in have.items():
179 179 if is_avail:
180 180 avail.append(k)
181 181 else:
182 182 not_avail.append(k)
183 183
184 184 if avail:
185 185 out.append('\nTools and libraries available at test time:\n')
186 186 avail.sort()
187 187 out.append(' ' + ' '.join(avail)+'\n')
188 188
189 189 if not_avail:
190 190 out.append('\nTools and libraries NOT available at test time:\n')
191 191 not_avail.sort()
192 192 out.append(' ' + ' '.join(not_avail)+'\n')
193 193
194 194 return ''.join(out)
195 195
196 196
197 197 def make_exclude():
198 198 """Make patterns of modules and packages to exclude from testing.
199 199
200 200 For the IPythonDoctest plugin, we need to exclude certain patterns that
201 201 cause testing problems. We should strive to minimize the number of
202 202 skipped modules, since this means untested code.
203 203
204 204 These modules and packages will NOT get scanned by nose at all for tests.
205 205 """
206 206 # Simple utility to make IPython paths more readably, we need a lot of
207 207 # these below
208 208 ipjoin = lambda *paths: pjoin('IPython', *paths)
209 209
210 210 exclusions = [ipjoin('external'),
211 211 ipjoin('quarantine'),
212 212 ipjoin('deathrow'),
213 213 # This guy is probably attic material
214 214 ipjoin('testing', 'mkdoctests'),
215 215 # Testing inputhook will need a lot of thought, to figure out
216 216 # how to have tests that don't lock up with the gui event
217 217 # loops in the picture
218 218 ipjoin('lib', 'inputhook'),
219 219 # Config files aren't really importable stand-alone
220 220 ipjoin('config', 'profile'),
221 221 # The notebook 'static' directory contains JS, css and other
222 222 # files for web serving. Occasionally projects may put a .py
223 223 # file in there (MathJax ships a conf.py), so we might as
224 224 # well play it safe and skip the whole thing.
225 225 ipjoin('html', 'static'),
226 226 ipjoin('html', 'fabfile'),
227 227 ]
228 228 if not have['sqlite3']:
229 229 exclusions.append(ipjoin('core', 'tests', 'test_history'))
230 230 exclusions.append(ipjoin('core', 'history'))
231 231 if not have['wx']:
232 232 exclusions.append(ipjoin('lib', 'inputhookwx'))
233 233
234 234 if 'IPython.kernel.inprocess' not in sys.argv:
235 235 exclusions.append(ipjoin('kernel', 'inprocess'))
236 236
237 237 # FIXME: temporarily disable autoreload tests, as they can produce
238 238 # spurious failures in subsequent tests (cythonmagic).
239 239 exclusions.append(ipjoin('extensions', 'autoreload'))
240 240 exclusions.append(ipjoin('extensions', 'tests', 'test_autoreload'))
241 241
242 242 # We do this unconditionally, so that the test suite doesn't import
243 243 # gtk, changing the default encoding and masking some unicode bugs.
244 244 exclusions.append(ipjoin('lib', 'inputhookgtk'))
245 245 exclusions.append(ipjoin('kernel', 'zmq', 'gui', 'gtkembed'))
246 246
247 247 #Also done unconditionally, exclude nbconvert directories containing
248 248 #config files used to test. Executing the config files with iptest would
249 249 #cause an exception.
250 250 exclusions.append(ipjoin('nbconvert', 'tests', 'files'))
251 251 exclusions.append(ipjoin('nbconvert', 'exporters', 'tests', 'files'))
252 252
253 253 # These have to be skipped on win32 because the use echo, rm, cd, etc.
254 254 # See ticket https://github.com/ipython/ipython/issues/87
255 255 if sys.platform == 'win32':
256 256 exclusions.append(ipjoin('testing', 'plugin', 'test_exampleip'))
257 257 exclusions.append(ipjoin('testing', 'plugin', 'dtexample'))
258 258
259 259 if not have['pexpect']:
260 260 exclusions.extend([ipjoin('lib', 'irunner'),
261 261 ipjoin('lib', 'tests', 'test_irunner'),
262 262 ipjoin('terminal', 'console'),
263 263 ])
264 264
265 265 if not have['zmq']:
266 266 exclusions.append(ipjoin('lib', 'kernel'))
267 267 exclusions.append(ipjoin('kernel'))
268 268 exclusions.append(ipjoin('qt'))
269 269 exclusions.append(ipjoin('html'))
270 270 exclusions.append(ipjoin('consoleapp.py'))
271 271 exclusions.append(ipjoin('terminal', 'console'))
272 272 exclusions.append(ipjoin('parallel'))
273 273 elif not have['qt'] or not have['pygments']:
274 274 exclusions.append(ipjoin('qt'))
275 275
276 276 if not have['pymongo']:
277 277 exclusions.append(ipjoin('parallel', 'controller', 'mongodb'))
278 278 exclusions.append(ipjoin('parallel', 'tests', 'test_mongodb'))
279 279
280 280 if not have['matplotlib']:
281 281 exclusions.extend([ipjoin('core', 'pylabtools'),
282 282 ipjoin('core', 'tests', 'test_pylabtools'),
283 283 ipjoin('kernel', 'zmq', 'pylab'),
284 284 ])
285 285
286 286 if not have['cython']:
287 287 exclusions.extend([ipjoin('extensions', 'cythonmagic')])
288 288 exclusions.extend([ipjoin('extensions', 'tests', 'test_cythonmagic')])
289 289
290 290 if not have['oct2py']:
291 291 exclusions.extend([ipjoin('extensions', 'octavemagic')])
292 292 exclusions.extend([ipjoin('extensions', 'tests', 'test_octavemagic')])
293 293
294 294 if not have['tornado']:
295 295 exclusions.append(ipjoin('html'))
296 exclusions.append(ipjoin('nbconvert', 'post_processors', 'serve'))
297 exclusions.append(ipjoin('nbconvert', 'post_processors', 'tests', 'test_serve'))
296 298
297 299 if not have['jinja2']:
298 300 exclusions.append(ipjoin('html', 'notebookapp'))
299 301
300 302 if not have['rpy2'] or not have['numpy']:
301 303 exclusions.append(ipjoin('extensions', 'rmagic'))
302 304 exclusions.append(ipjoin('extensions', 'tests', 'test_rmagic'))
303 305
304 306 if not have['azure']:
305 307 exclusions.append(ipjoin('html', 'services', 'notebooks', 'azurenbmanager'))
306 308
307 309 if not all((have['pygments'], have['jinja2'], have['sphinx'])):
308 310 exclusions.append(ipjoin('nbconvert'))
309 311
310 312 # This is needed for the reg-exp to match on win32 in the ipdoctest plugin.
311 313 if sys.platform == 'win32':
312 314 exclusions = [s.replace('\\','\\\\') for s in exclusions]
313 315
314 316 # check for any exclusions that don't seem to exist:
315 317 parent, _ = os.path.split(get_ipython_package_dir())
316 318 for exclusion in exclusions:
317 319 if exclusion.endswith(('deathrow', 'quarantine')):
318 320 # ignore deathrow/quarantine, which exist in dev, but not install
319 321 continue
320 322 fullpath = pjoin(parent, exclusion)
321 323 if not os.path.exists(fullpath) and not glob.glob(fullpath + '.*'):
322 324 warn("Excluding nonexistent file: %r" % exclusion)
323 325
324 326 return exclusions
325 327
326 328
327 329 class IPTester(object):
328 330 """Call that calls iptest or trial in a subprocess.
329 331 """
330 332 #: string, name of test runner that will be called
331 333 runner = None
332 334 #: list, parameters for test runner
333 335 params = None
334 336 #: list, arguments of system call to be made to call test runner
335 337 call_args = None
336 338 #: list, subprocesses we start (for cleanup)
337 339 processes = None
338 340 #: str, coverage xml output file
339 341 coverage_xml = None
340 342 buffer_output = False
341 343
342 344 def __init__(self, runner='iptest', params=None):
343 345 """Create new test runner."""
344 346 p = os.path
345 347 if runner == 'iptest':
346 348 iptest_app = os.path.abspath(get_ipython_module_path('IPython.testing.iptest'))
347 349 self.runner = pycmd2argv(iptest_app) + sys.argv[1:]
348 350 else:
349 351 raise Exception('Not a valid test runner: %s' % repr(runner))
350 352 if params is None:
351 353 params = []
352 354 if isinstance(params, str):
353 355 params = [params]
354 356 self.params = params
355 357
356 358 # Assemble call
357 359 self.call_args = self.runner+self.params
358 360
359 361 # Find the section we're testing (IPython.foo)
360 362 for sect in self.params:
361 363 if sect.startswith('IPython') or sect in special_test_suites: break
362 364 else:
363 365 raise ValueError("Section not found", self.params)
364 366
365 367 if '--with-xunit' in self.call_args:
366 368
367 369 self.call_args.append('--xunit-file')
368 370 # FIXME: when Windows uses subprocess.call, these extra quotes are unnecessary:
369 371 xunit_file = path.abspath(sect+'.xunit.xml')
370 372 if sys.platform == 'win32':
371 373 xunit_file = '"%s"' % xunit_file
372 374 self.call_args.append(xunit_file)
373 375
374 376 if '--with-xml-coverage' in self.call_args:
375 377 self.coverage_xml = path.abspath(sect+".coverage.xml")
376 378 self.call_args.remove('--with-xml-coverage')
377 379 self.call_args = ["coverage", "run", "--source="+sect] + self.call_args[1:]
378 380
379 381 # Store anything we start to clean up on deletion
380 382 self.processes = []
381 383
382 384 def _run_cmd(self):
383 385 with TemporaryDirectory() as IPYTHONDIR:
384 386 env = os.environ.copy()
385 387 env['IPYTHONDIR'] = IPYTHONDIR
386 388 # print >> sys.stderr, '*** CMD:', ' '.join(self.call_args) # dbg
387 389 output = subprocess.PIPE if self.buffer_output else None
388 390 subp = subprocess.Popen(self.call_args, stdout=output,
389 391 stderr=output, env=env)
390 392 self.processes.append(subp)
391 393 # If this fails, the process will be left in self.processes and
392 394 # cleaned up later, but if the wait call succeeds, then we can
393 395 # clear the stored process.
394 396 retcode = subp.wait()
395 397 self.processes.pop()
396 398 self.stdout = subp.stdout
397 399 self.stderr = subp.stderr
398 400 return retcode
399 401
400 402 def run(self):
401 403 """Run the stored commands"""
402 404 try:
403 405 retcode = self._run_cmd()
404 406 except KeyboardInterrupt:
405 407 return -signal.SIGINT
406 408 except:
407 409 import traceback
408 410 traceback.print_exc()
409 411 return 1 # signal failure
410 412
411 413 if self.coverage_xml:
412 414 subprocess.call(["coverage", "xml", "-o", self.coverage_xml])
413 415 return retcode
414 416
415 417 def __del__(self):
416 418 """Cleanup on exit by killing any leftover processes."""
417 419 for subp in self.processes:
418 420 if subp.poll() is not None:
419 421 continue # process is already dead
420 422
421 423 try:
422 424 print('Cleaning up stale PID: %d' % subp.pid)
423 425 subp.kill()
424 426 except: # (OSError, WindowsError) ?
425 427 # This is just a best effort, if we fail or the process was
426 428 # really gone, ignore it.
427 429 pass
428 430 else:
429 431 for i in range(10):
430 432 if subp.poll() is None:
431 433 time.sleep(0.1)
432 434 else:
433 435 break
434 436
435 437 if subp.poll() is None:
436 438 # The process did not die...
437 439 print('... failed. Manual cleanup may be required.')
438 440
439 441
440 442 special_test_suites = {
441 443 'autoreload': ['IPython.extensions.autoreload', 'IPython.extensions.tests.test_autoreload'],
442 444 }
443 445
444 446 def make_runners(inc_slow=False):
445 447 """Define the top-level packages that need to be tested.
446 448 """
447 449
448 450 # Packages to be tested via nose, that only depend on the stdlib
449 451 nose_pkg_names = ['config', 'core', 'extensions', 'lib', 'terminal',
450 452 'testing', 'utils', 'nbformat']
451 453
452 454 if have['qt']:
453 455 nose_pkg_names.append('qt')
454 456
455 457 if have['tornado']:
456 458 nose_pkg_names.append('html')
457 459
458 460 if have['zmq']:
459 461 nose_pkg_names.insert(0, 'kernel')
460 462 nose_pkg_names.insert(1, 'kernel.inprocess')
461 463 if inc_slow:
462 464 nose_pkg_names.insert(0, 'parallel')
463 465
464 466 if all((have['pygments'], have['jinja2'], have['sphinx'])):
465 467 nose_pkg_names.append('nbconvert')
466 468
467 469 # For debugging this code, only load quick stuff
468 470 #nose_pkg_names = ['core', 'extensions'] # dbg
469 471
470 472 # Make fully qualified package names prepending 'IPython.' to our name lists
471 473 nose_packages = ['IPython.%s' % m for m in nose_pkg_names ]
472 474
473 475 # Make runners
474 476 runners = [ (v, IPTester('iptest', params=v)) for v in nose_packages ]
475 477
476 478 for name in special_test_suites:
477 479 runners.append((name, IPTester('iptest', params=name)))
478 480
479 481 return runners
480 482
481 483
482 484 def run_iptest():
483 485 """Run the IPython test suite using nose.
484 486
485 487 This function is called when this script is **not** called with the form
486 488 `iptest all`. It simply calls nose with appropriate command line flags
487 489 and accepts all of the standard nose arguments.
488 490 """
489 491 # Apply our monkeypatch to Xunit
490 492 if '--with-xunit' in sys.argv and not hasattr(Xunit, 'orig_addError'):
491 493 monkeypatch_xunit()
492 494
493 495 warnings.filterwarnings('ignore',
494 496 'This will be removed soon. Use IPython.testing.util instead')
495 497
496 498 if sys.argv[1] in special_test_suites:
497 499 sys.argv[1:2] = special_test_suites[sys.argv[1]]
498 500 special_suite = True
499 501 else:
500 502 special_suite = False
501 503
502 504 argv = sys.argv + [ '--detailed-errors', # extra info in tracebacks
503 505
504 506 '--with-ipdoctest',
505 507 '--ipdoctest-tests','--ipdoctest-extension=txt',
506 508
507 509 # We add --exe because of setuptools' imbecility (it
508 510 # blindly does chmod +x on ALL files). Nose does the
509 511 # right thing and it tries to avoid executables,
510 512 # setuptools unfortunately forces our hand here. This
511 513 # has been discussed on the distutils list and the
512 514 # setuptools devs refuse to fix this problem!
513 515 '--exe',
514 516 ]
515 517 if '-a' not in argv and '-A' not in argv:
516 518 argv = argv + ['-a', '!crash']
517 519
518 520 if nose.__version__ >= '0.11':
519 521 # I don't fully understand why we need this one, but depending on what
520 522 # directory the test suite is run from, if we don't give it, 0 tests
521 523 # get run. Specifically, if the test suite is run from the source dir
522 524 # with an argument (like 'iptest.py IPython.core', 0 tests are run,
523 525 # even if the same call done in this directory works fine). It appears
524 526 # that if the requested package is in the current dir, nose bails early
525 527 # by default. Since it's otherwise harmless, leave it in by default
526 528 # for nose >= 0.11, though unfortunately nose 0.10 doesn't support it.
527 529 argv.append('--traverse-namespace')
528 530
529 531 # use our plugin for doctesting. It will remove the standard doctest plugin
530 532 # if it finds it enabled
531 533 ipdt = IPythonDoctest() if special_suite else IPythonDoctest(make_exclude())
532 534 plugins = [ipdt, KnownFailure()]
533 535
534 536 # We need a global ipython running in this process, but the special
535 537 # in-process group spawns its own IPython kernels, so for *that* group we
536 538 # must avoid also opening the global one (otherwise there's a conflict of
537 539 # singletons). Ultimately the solution to this problem is to refactor our
538 540 # assumptions about what needs to be a singleton and what doesn't (app
539 541 # objects should, individual shells shouldn't). But for now, this
540 542 # workaround allows the test suite for the inprocess module to complete.
541 543 if not 'IPython.kernel.inprocess' in sys.argv:
542 544 globalipapp.start_ipython()
543 545
544 546 # Now nose can run
545 547 TestProgram(argv=argv, addplugins=plugins)
546 548
547 549 def do_run(x):
548 550 print('IPython test group:',x[0])
549 551 ret = x[1].run()
550 552 return ret
551 553
552 554 def run_iptestall(inc_slow=False, fast=False):
553 555 """Run the entire IPython test suite by calling nose and trial.
554 556
555 557 This function constructs :class:`IPTester` instances for all IPython
556 558 modules and package and then runs each of them. This causes the modules
557 559 and packages of IPython to be tested each in their own subprocess using
558 560 nose.
559 561
560 562 Parameters
561 563 ----------
562 564
563 565 inc_slow : bool, optional
564 566 Include slow tests, like IPython.parallel. By default, these tests aren't
565 567 run.
566 568
567 569 fast : bool, option
568 570 Run the test suite in parallel, if True, using as many threads as there
569 571 are processors
570 572 """
571 573 if fast:
572 574 p = multiprocessing.pool.ThreadPool()
573 575 else:
574 576 p = multiprocessing.pool.ThreadPool(1)
575 577
576 578 runners = make_runners(inc_slow=inc_slow)
577 579
578 580 # Run the test runners in a temporary dir so we can nuke it when finished
579 581 # to clean up any junk files left over by accident. This also makes it
580 582 # robust against being run in non-writeable directories by mistake, as the
581 583 # temp dir will always be user-writeable.
582 584 curdir = os.getcwdu()
583 585 testdir = tempfile.gettempdir()
584 586 os.chdir(testdir)
585 587
586 588 # Run all test runners, tracking execution time
587 589 failed = []
588 590 t_start = time.time()
589 591
590 592 try:
591 593 all_res = p.map(do_run, runners)
592 594 print('*'*70)
593 595 for ((name, runner), res) in zip(runners, all_res):
594 596 tgroup = 'IPython test group: ' + name
595 597 res_string = 'OK' if res == 0 else 'FAILED'
596 598 res_string = res_string.rjust(70 - len(tgroup), '.')
597 599 print(tgroup + res_string)
598 600 if res:
599 601 failed.append( (name, runner) )
600 602 if res == -signal.SIGINT:
601 603 print("Interrupted")
602 604 break
603 605 finally:
604 606 os.chdir(curdir)
605 607 t_end = time.time()
606 608 t_tests = t_end - t_start
607 609 nrunners = len(runners)
608 610 nfail = len(failed)
609 611 # summarize results
610 612 print()
611 613 print('*'*70)
612 614 print('Test suite completed for system with the following information:')
613 615 print(report())
614 616 print('Ran %s test groups in %.3fs' % (nrunners, t_tests))
615 617 print()
616 618 print('Status:')
617 619 if not failed:
618 620 print('OK')
619 621 else:
620 622 # If anything went wrong, point out what command to rerun manually to
621 623 # see the actual errors and individual summary
622 624 print('ERROR - %s out of %s test groups failed.' % (nfail, nrunners))
623 625 for name, failed_runner in failed:
624 626 print('-'*40)
625 627 print('Runner failed:',name)
626 628 print('You may wish to rerun this one individually, with:')
627 629 failed_call_args = [py3compat.cast_unicode(x) for x in failed_runner.call_args]
628 630 print(u' '.join(failed_call_args))
629 631 print()
630 632 # Ensure that our exit code indicates failure
631 633 sys.exit(1)
632 634
633 635
634 636 def main():
635 637 for arg in sys.argv[1:]:
636 638 if arg.startswith('IPython') or arg in special_test_suites:
637 639 # This is in-process
638 640 run_iptest()
639 641 else:
640 642 inc_slow = "--all" in sys.argv
641 643 if inc_slow:
642 644 sys.argv.remove("--all")
643 645
644 646 fast = "--fast" in sys.argv
645 647 if fast:
646 648 sys.argv.remove("--fast")
647 649 IPTester.buffer_output = True
648 650
649 651 # This starts subprocesses
650 652 run_iptestall(inc_slow=inc_slow, fast=fast)
651 653
652 654
653 655 if __name__ == '__main__':
654 656 main()
@@ -1,232 +1,227 b''
1 1 .. _nbconvert:
2 2
3 3 Converting notebooks to other formats
4 4 =====================================
5 5
6 6 Newly added in the 1.0 release of IPython is the ``nbconvert`` tool, which
7 7 allows you to convert an ``.ipynb`` notebook document file into various static
8 8 formats.
9 9
10 10 Currently, ``nbconvert`` is provided as a command line tool, run as a script
11 11 using IPython. A direct export capability from within the
12 12 IPython Notebook web app is planned.
13 13
14 14 The command-line syntax to run the ``nbconvert`` script is::
15 15
16 16 $ ipython nbconvert --to FORMAT notebook.ipynb
17 17
18 18 This will convert the IPython document file ``notebook.ipynb`` into the output
19 19 format given by the ``FORMAT`` string.
20 20
21 21 The default output format is html, for which the ``--to`` argument may be
22 22 omitted::
23 23
24 24 $ ipython nbconvert notebook.ipynb
25 25
26 26 IPython provides a few templates for some output formats, and these can be
27 27 specified via an additional ``--template`` argument.
28 28
29 29 The currently supported export formats are:
30 30
31 31 * ``--to html``
32 32
33 33 - ``--template full`` (default)
34 34
35 35 A full static HTML render of the notebook.
36 36 This looks very similar to the interactive view.
37 37
38 38 - ``--template basic``
39 39
40 40 Simplified HTML, useful for embedding in webpages, blogs, etc.
41 41 This excludes HTML headers.
42 42
43 43 * ``--to latex``
44 44
45 45 Latex export. This generates ``NOTEBOOK_NAME.tex`` file,
46 46 ready for export. You can automatically run latex on it to generate a PDF
47 47 by adding ``--post PDF``.
48 48
49 49 - ``--template article`` (default)
50 50
51 51 Latex article, derived from Sphinx's howto template.
52 52
53 53 - ``--template book``
54 54
55 55 Latex book, derived from Sphinx's manual template.
56 56
57 57 - ``--template basic``
58 58
59 59 Very basic latex output - mainly meant as a starting point for custom templates.
60 60
61 61 * ``--to slides``
62 62
63 63 This generates a Reveal.js HTML slideshow.
64 It must be served by an HTTP server. The easiest way to get this is to add
64 It must be served by an HTTP server. The easiest way to do this is adding
65 65 ``--post serve`` on the command-line.
66 If you want to use the speaker notes plugin, just add
67 ``--slide-notes=True`` on the command-line.
68 For low connectivity environments, you can use a local copy of the reveal.js library,
69 just add ``--offline-slides=reveal.js`` on the command-line, and do not forget to move
70 your downloaded ``reveal.js`` library to the same folder where your slides are located.
71
66
72 67 * ``--to markdown``
73 68
74 69 Simple markdown output. Markdown cells are unaffected,
75 70 and code cells are placed in triple-backtick (```````) blocks.
76 71
77 72 * ``--to rst``
78 73
79 74 Basic reStructuredText output. Useful as a starting point for embedding notebooks
80 75 in Sphinx docs.
81 76
82 77 * ``--to python``
83 78
84 79 Convert a notebook to an executable Python script.
85 80 This is the simplest way to get a Python script out of a notebook.
86 81 If there were any magics in the notebook, this may only be executable from
87 82 an IPython session.
88 83
89 84 .. note::
90 85
91 86 nbconvert uses pandoc_ to convert between various markup languages,
92 87 so pandoc is a dependency of most nbconvert transforms,
93 88 excluding Markdown and Python.
94 89
95 90 .. _pandoc: http://johnmacfarlane.net/pandoc/
96 91
97 92 The output file created by ``nbconvert`` will have the same base name as
98 93 the notebook and will be placed in the current working directory. Any
99 94 supporting files (graphics, etc) will be placed in a new directory with the
100 95 same base name as the notebook, suffixed with ``_files``::
101 96
102 97 $ ipython nbconvert notebook.ipynb
103 98 $ ls
104 99 notebook.ipynb notebook.html notebook_files/
105 100
106 101 For simple single-file output, such as html, markdown, etc.,
107 102 the output may be sent to standard output with::
108 103
109 104 $ ipython nbconvert --to markdown notebook.ipynb --stdout
110 105
111 106 Multiple notebooks can be specified from the command line::
112 107
113 108 $ ipython nbconvert notebook*.ipynb
114 109 $ ipython nbconvert notebook1.ipynb notebook2.ipynb
115 110
116 111 or via a list in a configuration file, say ``mycfg.py``, containing the text::
117 112
118 113 c = get_config()
119 114 c.NbConvertApp.notebooks = ["notebook1.ipynb", "notebook2.ipynb"]
120 115
121 116 and using the command::
122 117
123 118 $ ipython nbconvert --config mycfg.py
124 119
125 120
126 121 .. _notebook_format:
127 122
128 123 LaTeX citations
129 124 ---------------
130 125
131 126 ``nbconvert`` now has support for LaTeX citations. With this capability you
132 127 can:
133 128
134 129 * Manage citations using BibTeX.
135 130 * Cite those citations in Markdown cells using HTML data attributes.
136 131 * Have ``nbconvert`` generate proper LaTeX citations and run BibTeX.
137 132
138 133 For an example of how this works, please see the citations example in
139 134 the nbconvert-examples_ repository.
140 135
141 136 .. _nbconvert-examples: https://github.com/ipython/nbconvert-examples
142 137
143 138 Notebook JSON file format
144 139 -------------------------
145 140
146 141 Notebook documents are JSON files with an ``.ipynb`` extension, formatted
147 142 as legibly as possible with minimal extra indentation and cell content broken
148 143 across lines to make them reasonably friendly to use in version-control
149 144 workflows. You should be very careful if you ever manually edit this JSON
150 145 data, as it is extremely easy to corrupt its internal structure and make the
151 146 file impossible to load. In general, you should consider the notebook as a
152 147 file meant only to be edited by the IPython Notebook app itself, not for
153 148 hand-editing.
154 149
155 150 .. note::
156 151
157 152 Binary data such as figures are also saved directly in the JSON file.
158 153 This provides convenient single-file portability, but means that the
159 154 files can be large; a ``diff`` of binary data is also not very
160 155 meaningful. Since the binary blobs are encoded in a single line, they
161 156 affect only one line of the ``diff`` output, but they are typically very
162 157 long lines. You can use the ``Cell | All Output | Clear`` menu option to
163 158 remove all output from a notebook prior to committing it to version
164 159 control, if this is a concern.
165 160
166 161 The notebook server can also generate a pure Python version of your notebook,
167 162 using the ``File | Download as`` menu option. The resulting ``.py`` file will
168 163 contain all the code cells from your notebook verbatim, and all Markdown cells
169 164 prepended with a comment marker. The separation between code and Markdown
170 165 cells is indicated with special comments and there is a header indicating the
171 166 format version. All output is removed when exporting to Python.
172 167
173 168 As an example, consider a simple notebook called ``simple.ipynb`` which
174 169 contains one Markdown cell, with the content ``The simplest notebook.``, one
175 170 code input cell with the content ``print "Hello, IPython!"``, and the
176 171 corresponding output.
177 172
178 173 The contents of the notebook document ``simple.ipynb`` is the following JSON
179 174 container::
180 175
181 176 {
182 177 "metadata": {
183 178 "name": "simple"
184 179 },
185 180 "nbformat": 3,
186 181 "nbformat_minor": 0,
187 182 "worksheets": [
188 183 {
189 184 "cells": [
190 185 {
191 186 "cell_type": "markdown",
192 187 "metadata": {},
193 188 "source": "The simplest notebook."
194 189 },
195 190 {
196 191 "cell_type": "code",
197 192 "collapsed": false,
198 193 "input": "print \"Hello, IPython\"",
199 194 "language": "python",
200 195 "metadata": {},
201 196 "outputs": [
202 197 {
203 198 "output_type": "stream",
204 199 "stream": "stdout",
205 200 "text": "Hello, IPython\n"
206 201 }
207 202 ],
208 203 "prompt_number": 1
209 204 }
210 205 ],
211 206 "metadata": {}
212 207 }
213 208 ]
214 209 }
215 210
216 211
217 212 The corresponding Python script is::
218 213
219 214 # -*- coding: utf-8 -*-
220 215 # <nbformat>3.0</nbformat>
221 216
222 217 # <markdowncell>
223 218
224 219 # The simplest notebook.
225 220
226 221 # <codecell>
227 222
228 223 print "Hello, IPython"
229 224
230 225 Note that indeed the output of the code cell, which is present in the JSON
231 226 container, has been removed in the ``.py`` script.
232 227
General Comments 0
You need to be logged in to leave comments. Login now