##// END OF EJS Templates
Moved PDF logic into Post-Processor class
Jonathan Frederic -
Show More
@@ -0,0 +1,2 b''
1 from .base import PostProcessorBase
2 from .pdf import PDFPostProcessor
@@ -0,0 +1,36 b''
1 #!/usr/bin/env python
2 """
3 Basic post processor
4 """
5 #-----------------------------------------------------------------------------
6 #Copyright (c) 2013, the IPython Development Team.
7 #
8 #Distributed under the terms of the Modified BSD License.
9 #
10 #The full license is in the file COPYING.txt, distributed with this software.
11 #-----------------------------------------------------------------------------
12
13 #-----------------------------------------------------------------------------
14 # Imports
15 #-----------------------------------------------------------------------------
16
17 from ..utils.base import NbConvertBase
18
19
20 #-----------------------------------------------------------------------------
21 # Classes
22 #-----------------------------------------------------------------------------
23 class PostProcessorBase(NbConvertBase):
24
25 def __call__(self, input):
26 """
27 See def call() ...
28 """
29 self.call(input)
30
31
32 def call(self, input):
33 """
34 Post-process output from a writer.
35 """
36 raise NotImplementedError('call')
@@ -1,6 +1,7 b''
1 1 """Utilities for converting notebooks to and from different formats."""
2 2
3 3 from .exporters import *
4 4 import filters
5 5 import transformers
6 import post_processors
6 7 import writers
@@ -1,258 +1,300 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 import sys
21 21 import os
22 22 import glob
23 23
24 24 # From IPython
25 25 from IPython.core.application import BaseIPythonApplication, base_aliases, base_flags
26 26 from IPython.config import catch_config_error, Configurable
27 27 from IPython.utils.traitlets import (
28 28 Unicode, List, Instance, DottedObjectName, Type, CaselessStrEnum,
29 29 )
30 30 from IPython.utils.importstring import import_item
31 31
32 32 from .exporters.export import export_by_name, get_export_names, ExporterNameError
33 from IPython.nbconvert import exporters, transformers, writers
33 from IPython.nbconvert import exporters, transformers, writers, post_processors
34 34 from .utils.base import NbConvertBase
35 35 from .utils.exceptions import ConversionException
36 36
37 37 #-----------------------------------------------------------------------------
38 38 #Classes and functions
39 39 #-----------------------------------------------------------------------------
40 40
41 class DottedOrNone(DottedObjectName):
42 """
43 A string holding a valid dotted object name in Python, such as A.b3._c
44 Also allows for None type."""
45
46 default_value = u''
47
48 def validate(self, obj, value):
49 if value is not None and len(value) > 0:
50 return super(DottedOrNone, self).validate(obj, value)
51 else:
52 return value
53
41 54 nbconvert_aliases = {}
42 55 nbconvert_aliases.update(base_aliases)
43 56 nbconvert_aliases.update({
44 57 'to' : 'NbConvertApp.export_format',
45 58 'template' : 'Exporter.template_file',
46 59 'notebooks' : 'NbConvertApp.notebooks',
47 60 'writer' : 'NbConvertApp.writer_class',
61 'post': 'NbConvertApp.post_processor_class'
48 62 })
49 63
50 64 nbconvert_flags = {}
51 65 nbconvert_flags.update(base_flags)
52 66 nbconvert_flags.update({
53 67 'stdout' : (
54 68 {'NbConvertApp' : {'writer_class' : "StdoutWriter"}},
55 69 "Write notebook output to stdout instead of files."
56 ),
57
58 'pdf' : (
59 {'NbConvertApp' : {'writer_class' : "PDFWriter"}},
60 "Compile notebook output to a PDF (requires `--to latex`)."
61 70 )
62 71 })
63 72
64 73
65 74 class NbConvertApp(BaseIPythonApplication):
66 75 """Application used to convert to and from notebook file type (*.ipynb)"""
67 76
68 77 name = 'ipython-nbconvert'
69 78 aliases = nbconvert_aliases
70 79 flags = nbconvert_flags
71 80
72 81 def _classes_default(self):
73 82 classes = [NbConvertBase]
74 83 for pkg in (exporters, transformers, writers):
75 84 for name in dir(pkg):
76 85 cls = getattr(pkg, name)
77 86 if isinstance(cls, type) and issubclass(cls, Configurable):
78 87 classes.append(cls)
79 88 return classes
80 89
81 90 description = Unicode(
82 91 u"""This application is used to convert notebook files (*.ipynb)
83 92 to various other formats.""")
84 93
85 94 examples = Unicode(u"""
86 95 The simplest way to use nbconvert is
87 96
88 97 > ipython nbconvert mynotebook.ipynb
89 98
90 99 which will convert mynotebook.ipynb to the default format (probably HTML).
91 100
92 101 You can specify the export format with `--to`.
93 102 Options include {0}
94 103
95 104 > ipython nbconvert --to latex mynotebook.ipnynb
96 105
97 106 Both HTML and LaTeX support multiple output templates. LaTeX includes
98 107 'basic', 'book', and 'article'. HTML includes 'basic' and 'full'. You
99 108 can specify the flavor of the format used.
100 109
101 110 > ipython nbconvert --to html --template reveal mynotebook.ipnynb
102 111
103 112 You can also pipe the output to stdout, rather than a file
104 113
105 114 > ipython nbconvert mynotebook.ipynb --stdout
106 115
107 116 or to a PDF
108 117
109 118 > ipython nbconvert mynotebook.ipynb --to latex --pdf
110 119
111 120 Multiple notebooks can be given at the command line in a couple of
112 121 different ways:
113 122
114 123 > ipython nbconvert notebook*.ipynb
115 124 > ipython nbconvert notebook1.ipynb notebook2.ipynb
116 125
117 126 or you can specify the notebooks list in a config file, containing::
118 127
119 128 c.NbConvertApp.notebooks = ["my_notebook.ipynb"]
120 129
121 130 > ipython nbconvert --config mycfg.py
122 131 """.format(get_export_names()))
132
123 133 # Writer specific variables
124 134 writer = Instance('IPython.nbconvert.writers.base.WriterBase',
125 135 help="""Instance of the writer class used to write the
126 136 results of the conversion.""")
127 137 writer_class = DottedObjectName('FilesWriter', config=True,
128 138 help="""Writer class used to write the
129 139 results of the conversion""")
130 140 writer_aliases = {'FilesWriter': 'IPython.nbconvert.writers.files.FilesWriter',
131 141 'PDFWriter': 'IPython.nbconvert.writers.pdf.PDFWriter',
132 142 'DebugWriter': 'IPython.nbconvert.writers.debug.DebugWriter',
133 143 'StdoutWriter': 'IPython.nbconvert.writers.stdout.StdoutWriter'}
134 144 writer_factory = Type()
135 145
136 146 def _writer_class_changed(self, name, old, new):
137 147 if new in self.writer_aliases:
138 148 new = self.writer_aliases[new]
139 149 self.writer_factory = import_item(new)
140 150
151 # Post-processor specific variables
152 post_processor = Instance('IPython.nbconvert.post_processors.base.PostProcessorBase',
153 help="""Instance of the PostProcessor class used to write the
154 results of the conversion.""")
155
156 post_processor_class = DottedOrNone(config=True,
157 help="""PostProcessor class used to write the
158 results of the conversion""")
159 post_processor_aliases = {'PDF': 'IPython.nbconvert.post_processors.pdf.PDFPostProcessor'}
160 post_processor_factory = Type()
161
162 def _post_processor_class_changed(self, name, old, new):
163 if new in self.post_processor_aliases:
164 new = self.post_processor_aliases[new]
165 if new:
166 self.post_processor_factory = import_item(new)
167
141 168
142 169 # Other configurable variables
143 170 export_format = CaselessStrEnum(get_export_names(),
144 171 default_value="html",
145 172 config=True,
146 173 help="""The export format to be used."""
147 174 )
148 175
149 176 notebooks = List([], config=True, help="""List of notebooks to convert.
150 177 Wildcards are supported.
151 178 Filenames passed positionally will be added to the list.
152 179 """)
153 180
154 181 @catch_config_error
155 182 def initialize(self, argv=None):
156 183 super(NbConvertApp, self).initialize(argv)
157 184 self.init_syspath()
158 185 self.init_notebooks()
159 186 self.init_writer()
187 self.init_post_processor()
188
160 189
161 190
162 191 def init_syspath(self):
163 192 """
164 193 Add the cwd to the sys.path ($PYTHONPATH)
165 194 """
166 195 sys.path.insert(0, os.getcwd())
167 196
168 197
169 198 def init_notebooks(self):
170 199 """Construct the list of notebooks.
171 200 If notebooks are passed on the command-line,
172 201 they override notebooks specified in config files.
173 202 Glob each notebook to replace notebook patterns with filenames.
174 203 """
175 204
176 205 # Specifying notebooks on the command-line overrides (rather than adds)
177 206 # the notebook list
178 207 if self.extra_args:
179 208 patterns = self.extra_args
180 209 else:
181 210 patterns = self.notebooks
182 211
183 212 # Use glob to replace all the notebook patterns with filenames.
184 213 filenames = []
185 214 for pattern in patterns:
186 215
187 216 # Use glob to find matching filenames. Allow the user to convert
188 217 # notebooks without having to type the extension.
189 218 globbed_files = glob.glob(pattern)
190 219 globbed_files.extend(glob.glob(pattern + '.ipynb'))
191 220
192 221 for filename in globbed_files:
193 222 if not filename in filenames:
194 223 filenames.append(filename)
195 224 self.notebooks = filenames
196 225
197 226 def init_writer(self):
198 227 """
199 228 Initialize the writer (which is stateless)
200 229 """
201 230 self._writer_class_changed(None, self.writer_class, self.writer_class)
202 231 self.writer = self.writer_factory(parent=self)
203 232
233 def init_post_processor(self):
234 """
235 Initialize the post_processor (which is stateless)
236 """
237 self._post_processor_class_changed(None, self.post_processor_class,
238 self.post_processor_class)
239 if self.post_processor_factory:
240 self.post_processor = self.post_processor_factory(parent=self)
241
204 242 def start(self):
205 243 """
206 244 Ran after initialization completed
207 245 """
208 246 super(NbConvertApp, self).start()
209 247 self.convert_notebooks()
210 248
211 249 def convert_notebooks(self):
212 250 """
213 251 Convert the notebooks in the self.notebook traitlet
214 252 """
215 253 # Export each notebook
216 254 conversion_success = 0
217 255 for notebook_filename in self.notebooks:
218 256
219 257 # Get a unique key for the notebook and set it in the resources object.
220 258 basename = os.path.basename(notebook_filename)
221 259 notebook_name = basename[:basename.rfind('.')]
222 260 resources = {}
223 261 resources['unique_key'] = notebook_name
224 262 resources['output_files_dir'] = '%s_files' % notebook_name
225 263
226 264 # Try to export
227 265 try:
228 266 output, resources = export_by_name(self.export_format,
229 267 notebook_filename,
230 268 resources=resources,
231 269 config=self.config)
232 270 except ExporterNameError as e:
233 271 print("Error while converting '%s': '%s' exporter not found."
234 272 %(notebook_filename, self.export_format),
235 273 file=sys.stderr)
236 274 print("Known exporters are:",
237 275 "\n\t" + "\n\t".join(get_export_names()),
238 276 file=sys.stderr)
239 277 self.exit(1)
240 278 except ConversionException as e:
241 279 print("Error while converting '%s': %s" %(notebook_filename, e),
242 280 file=sys.stderr)
243 281 self.exit(1)
244 282 else:
245 self.writer.write(output, resources, notebook_name=notebook_name)
283 write_resultes = self.writer.write(output, resources, notebook_name=notebook_name)
284
285 #Post-process if post processor has been defined.
286 if hasattr(self, 'post_processor') and self.post_processor:
287 self.post_processor(write_resultes)
246 288 conversion_success += 1
247 289
248 290 # If nothing was converted successfully, help the user.
249 291 if conversion_success == 0:
250 292 self.print_help()
251 293 sys.exit(-1)
252 294
253 295
254 296 #-----------------------------------------------------------------------------
255 297 # Main entry point
256 298 #-----------------------------------------------------------------------------
257 299
258 300 launch_new_instance = NbConvertApp.launch_instance
@@ -1,47 +1,52 b''
1 1 #!/usr/bin/env python
2 2 """
3 3 Contains writer for writing nbconvert output to PDF.
4 4 """
5 5 #-----------------------------------------------------------------------------
6 6 #Copyright (c) 2013, the IPython Development Team.
7 7 #
8 8 #Distributed under the terms of the Modified BSD License.
9 9 #
10 10 #The full license is in the file COPYING.txt, distributed with this software.
11 11 #-----------------------------------------------------------------------------
12 12
13 13 #-----------------------------------------------------------------------------
14 14 # Imports
15 15 #-----------------------------------------------------------------------------
16 16
17 17 import subprocess
18 18 import os
19 19
20 from IPython.utils.traitlets import Integer, Unicode
20 from IPython.utils.traitlets import Integer, Unicode, Bool
21 21
22 from .files import FilesWriter
22 from .base import PostProcessorBase
23 23
24 24 #-----------------------------------------------------------------------------
25 25 # Classes
26 26 #-----------------------------------------------------------------------------
27 class PDFWriter(FilesWriter):
27 class PDFPostProcessor(PostProcessorBase):
28 28 """Writer designed to write to PDF files"""
29 29
30 30 iteration_count = Integer(3, config=True, help="""
31 31 How many times pdflatex will be called.
32 32 """)
33 33
34 34 compiler = Unicode(u'pdflatex {0}', config=True, help="""
35 35 Shell command used to compile PDF.""")
36 36
37 def write(self, output, resources, notebook_name=None, **kw):
37 verbose = Bool(False, config=True, help="""
38 Whether or not to display the output of the compile call.
39 """)
40
41 def call(self, input):
38 42 """
39 43 Consume and write Jinja output a PDF.
40 44 See files.py for more...
41 45 """
42 dest = super(PDFWriter, self).write(output, resources,
43 notebook_name=notebook_name, **kw)
44 command = self.compiler.format(dest)
45
46 command = self.compiler.format(input)
46 47 for index in range(self.iteration_count):
47 subprocess.Popen(command, shell=True, stdout=open(os.devnull, 'wb'))
48 if self.verbose:
49 subprocess.Popen(command, shell=True)
50 else:
51 with open(os.devnull, 'wb') as null:
52 subprocess.Popen(command, shell=True, stdout=null)
@@ -1,135 +1,149 b''
1 1 """
2 2 Contains tests for the nbconvertapp
3 3 """
4 4 #-----------------------------------------------------------------------------
5 5 #Copyright (c) 2013, the IPython Development Team.
6 6 #
7 7 #Distributed under the terms of the Modified BSD License.
8 8 #
9 9 #The full license is in the file COPYING.txt, distributed with this software.
10 10 #-----------------------------------------------------------------------------
11 11
12 12 #-----------------------------------------------------------------------------
13 13 # Imports
14 14 #-----------------------------------------------------------------------------
15 15
16 16 import os
17 17 from .base import TestsBase
18 18
19 19 from IPython.utils import py3compat
20 from IPython.testing import decorators as dec
20 21
21 22
22 23 #-----------------------------------------------------------------------------
23 24 # Constants
24 25 #-----------------------------------------------------------------------------
25 26
26 27 # Define ipython commandline name
27 28 if py3compat.PY3:
28 29 IPYTHON = 'ipython3'
29 30 else:
30 31 IPYTHON = 'ipython'
31 32
32 33
33 34 #-----------------------------------------------------------------------------
34 35 # Classes and functions
35 36 #-----------------------------------------------------------------------------
36 37
37 38 class TestNbConvertApp(TestsBase):
38 39 """Collection of NbConvertApp tests"""
39 40
40 41
41 42 def test_notebook_help(self):
42 43 """
43 44 Will help show if no notebooks are specified?
44 45 """
45 46 with self.create_temp_cwd():
46 47 assert "see '--help-all'" in self.call([IPYTHON, 'nbconvert'])
47 48
48 49
49 50 def test_glob(self):
50 51 """
51 52 Do search patterns work for notebook names?
52 53 """
53 54 with self.create_temp_cwd(['notebook*.ipynb']):
54 55 assert not 'error' in self.call([IPYTHON, 'nbconvert',
55 56 '--to="python"', '--notebooks=["*.ipynb"]']).lower()
56 57 assert os.path.isfile('notebook1.py')
57 58 assert os.path.isfile('notebook2.py')
58 59
59 60
60 61 def test_glob_subdir(self):
61 62 """
62 63 Do search patterns work for subdirectory notebook names?
63 64 """
64 65 with self.create_temp_cwd() as cwd:
65 66 self.copy_files_to(['notebook*.ipynb'], 'subdir/')
66 67 assert not 'error' in self.call([IPYTHON, 'nbconvert', '--to="python"',
67 68 '--notebooks=["%s"]' % os.path.join('subdir', '*.ipynb')]).lower()
68 69 assert os.path.isfile('notebook1.py')
69 70 assert os.path.isfile('notebook2.py')
70 71
71 72
72 73 def test_explicit(self):
73 74 """
74 75 Do explicit notebook names work?
75 76 """
76 77 with self.create_temp_cwd(['notebook*.ipynb']):
77 78 assert not 'error' in self.call([IPYTHON, 'nbconvert', '--to="python"',
78 79 '--notebooks=["notebook2.ipynb"]']).lower()
79 80 assert not os.path.isfile('notebook1.py')
80 81 assert os.path.isfile('notebook2.py')
81 82
82 83
84 #@dec.skip_known_failure
85 def test_post_processor(self):
86 """
87 Do post processors work?
88 """
89 with self.create_temp_cwd(['notebook1.ipynb']):
90 assert not 'error' in self.call([IPYTHON, 'nbconvert', '--to="latex"',
91 'notebook1', '--post="PDF"', 'PDFPostProcessor.verbose=True']).lower()
92 assert os.path.isfile('notebook1.tex')
93 print("\n\n\t" + "\n\t".join([f for f in os.listdir('.') if os.path.isfile(f)]) + "\n\n")
94 assert os.path.isfile('notebook1.pdf')
95
96
83 97 def test_template(self):
84 98 """
85 99 Do export templates work?
86 100 """
87 101 with self.create_temp_cwd(['notebook*.ipynb']):
88 102 assert not 'error' in self.call([IPYTHON, 'nbconvert', '--to="slides"',
89 103 '--notebooks=["notebook2.ipynb"]', '--template="reveal"']).lower()
90 104 assert os.path.isfile('notebook2.html')
91 105 with open('notebook2.html') as f:
92 106 assert '/reveal.css' in f.read()
93 107
94 108
95 109 def test_glob_explicit(self):
96 110 """
97 111 Can a search pattern be used along with matching explicit notebook names?
98 112 """
99 113 with self.create_temp_cwd(['notebook*.ipynb']):
100 114 assert not 'error' in self.call([IPYTHON, 'nbconvert', '--to="python"',
101 115 '--notebooks=["*.ipynb", "notebook1.ipynb", "notebook2.ipynb"]']).lower()
102 116 assert os.path.isfile('notebook1.py')
103 117 assert os.path.isfile('notebook2.py')
104 118
105 119
106 120 def test_explicit_glob(self):
107 121 """
108 122 Can explicit notebook names be used and then a matching search pattern?
109 123 """
110 124 with self.create_temp_cwd(['notebook*.ipynb']):
111 125 assert not 'error' in self.call([IPYTHON, 'nbconvert', '--to="python"',
112 126 '--notebooks=["notebook1.ipynb", "notebook2.ipynb", "*.ipynb"]']).lower()
113 127 assert os.path.isfile('notebook1.py')
114 128 assert os.path.isfile('notebook2.py')
115 129
116 130
117 131 def test_default_config(self):
118 132 """
119 133 Does the default config work?
120 134 """
121 135 with self.create_temp_cwd(['notebook*.ipynb', 'ipython_nbconvert_config.py']):
122 136 assert not 'error' in self.call([IPYTHON, 'nbconvert']).lower()
123 137 assert os.path.isfile('notebook1.py')
124 138 assert not os.path.isfile('notebook2.py')
125 139
126 140
127 141 def test_override_config(self):
128 142 """
129 143 Can the default config be overriden?
130 144 """
131 145 with self.create_temp_cwd(['notebook*.ipynb', 'ipython_nbconvert_config.py',
132 146 'override.py']):
133 147 assert not 'error' in self.call([IPYTHON, 'nbconvert', '--config="override.py"']).lower()
134 148 assert not os.path.isfile('notebook1.py')
135 149 assert os.path.isfile('notebook2.py')
General Comments 0
You need to be logged in to leave comments. Login now