##// END OF EJS Templates
Refactoring for the rename of ConverterTemplate to Exporter.
Jonathan Frederic -
Show More
@@ -1,255 +1,249 b''
1 """Base classes for the notebook conversion pipeline.
1 """Exporter for the notebook conversion pipeline.
2 2
3 This module defines ConverterTemplate, a highly configurable converter
4 that uses Jinja2 to convert notebook files into different format.
3 This module defines Exporter, a highly configurable converter
4 that uses Jinja2 to export notebook files into different format.
5 5
6 6 You can register both pre-transformers that will act on the notebook format
7 7 befor conversion and jinja filter that would then be availlable in the templates
8 8 """
9 9
10 from __future__ import absolute_import
11
12 10 #-----------------------------------------------------------------------------
13 11 # Copyright (c) 2013, the IPython Development Team.
14 12 #
15 13 # Distributed under the terms of the Modified BSD License.
16 14 #
17 15 # The full license is in the file COPYING.txt, distributed with this software.
18 16 #-----------------------------------------------------------------------------
19 17
20 18 #-----------------------------------------------------------------------------
21 19 # Imports
22 20 #-----------------------------------------------------------------------------
23
24 from __future__ import print_function
25 from __future__ import absolute_import
21 from __future__ import print_function, absolute_import
26 22
27 23 # Stdlib imports
28 24 import io
29 25 import os
30 26
31 27 # IPython imports
32 from IPython.utils.traitlets import MetaHasTraits
33 from IPython.utils.traitlets import (Unicode, List, Bool)
34 28 from IPython.config.configurable import Configurable
35 29 from IPython.nbformat import current as nbformat
36
30 from IPython.utils.traitlets import MetaHasTraits, Unicode, List, Bool
37 31
38 32 # other libs/dependencies
39 33 from jinja2 import Environment, FileSystemLoader
40 34
41
42 35 # local import (pre-transformers)
43 from . import transformers as trans
36 from . import transformers as trans #TODO
37
44 38 try:
45 from .sphinx_transformer import (SphinxTransformer)
39 from .sphinx_transformer import (SphinxTransformer) #TODO
46 40 except ImportError:
47 41 SphinxTransformer = None
48 42
49 from .latex_transformer import (LatexTransformer)
43 from .latex_transformer import (LatexTransformer) #TODO
50 44
51 45 # some jinja filters
52 46 from .jinja_filters import (python_comment, indent,
53 47 rm_fake, remove_ansi, markdown, highlight, highlight2latex,
54 48 ansi2html, markdown2latex, get_lines, escape_tex, FilterDataType,
55 49 rm_dollars, rm_math_space
56 50 )
57 51
58 from .utils import markdown2rst
52 from .utils import markdown2rst #TODO
59 53
60 import textwrap
54 import textwrap #TODO
61 55
62 56 def wrap(text, width=100):
63 57 """ try to detect and wrap paragraph"""
64 58 splitt = text.split('\n')
65 59 wrp = map(lambda x:textwrap.wrap(x,width),splitt)
66 60 wrpd = map('\n'.join, wrp)
67 61 return '\n'.join(wrpd)
68 62
69 63
70 64
71 65 # define differents environemnt with different
72 66 # delimiters not to conflict with languages inside
73 67
74 68 env = Environment(
75 69 loader=FileSystemLoader([
76 70 os.path.dirname(os.path.realpath(__file__))+'/../templates/',
77 71 os.path.dirname(os.path.realpath(__file__))+'/../templates/skeleton/',
78 72 ]),
79 73 extensions=['jinja2.ext.loopcontrols']
80 74 )
81 75
82 76
83 77 texenv = Environment(
84 78 loader=FileSystemLoader([
85 79 os.path.dirname(os.path.realpath(__file__))+'/../templates/tex/',
86 80 os.path.dirname(os.path.realpath(__file__))+'/../templates/skeleton/tex/',
87 81 ]),
88 82 extensions=['jinja2.ext.loopcontrols']
89 83 )
90 84
91 85
92 86 texenv.block_start_string = '((*'
93 87 texenv.block_end_string = '*))'
94 88
95 89 texenv.variable_start_string = '((('
96 90 texenv.variable_end_string = ')))'
97 91
98 92 texenv.comment_start_string = '((='
99 93 texenv.comment_end_string = '=))'
100 94
101 95 texenv.filters['escape_tex'] = escape_tex
102 96
103 97 #-----------------------------------------------------------------------------
104 98 # Class declarations
105 99 #-----------------------------------------------------------------------------
106 100 class ConversionException(Exception):
107 101 pass
108 102
109 class ConverterTemplate(Configurable):
103 class Exporter(Configurable):
110 104 """ A Jinja2 base converter templates
111 105
112 106 Preprocess the ipynb files, feed it throug jinja templates,
113 107 and spit an converted files and a data object with other data
114 108
115 109 should be mostly configurable
116 110 """
117 111
118 112 pre_transformer_order = List(['haspyout_transformer'],
119 113 config=True,
120 114 help= """
121 115 An ordered list of pre transformer to apply to the ipynb
122 116 file before running through templates
123 117 """
124 118 )
125 119
126 120 tex_environement = Bool(False,
127 121 config=True,
128 122 help=""" is this a tex environment or not """)
129 123
130 124 template_file = Unicode('',
131 125 config=True,
132 126 help=""" Name of the template file to use """ )
133 127 #-------------------------------------------------------------------------
134 128 # Instance-level attributes that are set in the constructor for this
135 129 # class.
136 130 #-------------------------------------------------------------------------
137 131
138 132
139 133 preprocessors = []
140 134
141 135 def __init__(self, preprocessors={}, jinja_filters={}, config=None, **kw):
142 136 """ Init a new converter.
143 137
144 138 config: the Configurable config object to pass around.
145 139
146 140 preprocessors: dict of **availlable** key/value function to run on
147 141 ipynb json data before conversion to extract/inline file.
148 142 See `transformer.py` and `ConfigurableTransformers`
149 143
150 144 set the order in which the transformers should apply
151 145 with the `pre_transformer_order` trait of this class
152 146
153 147 transformers registerd by this key will take precedence on
154 148 default one.
155 149
156 150
157 151 jinja_filters: dict of supplementary jinja filter that should be made
158 152 availlable in template. If those are of Configurable Class type,
159 153 they will be instanciated with the config object as argument.
160 154
161 155 user defined filter will overwrite the one availlable by default.
162 156 """
163 157 super(ConverterTemplate, self).__init__(config=config, **kw)
164 158
165 159 # variable parameters depending on the pype of jinja environement
166 160 self.env = texenv if self.tex_environement else env
167 161 self.ext = '.tplx' if self.tex_environement else '.tpl'
168 162
169 163 for name in self.pre_transformer_order:
170 164 # get the user-defined transformer first
171 165 transformer = preprocessors.get(name, getattr(trans, name, None))
172 166 if isinstance(transformer, MetaHasTraits):
173 167 transformer = transformer(config=config)
174 168 self.preprocessors.append(transformer)
175 169
176 170 ## for compat, remove later
177 171 self.preprocessors.append(trans.coalesce_streams)
178 172 self.preprocessors.append(trans.ExtractFigureTransformer(config=config))
179 173 self.preprocessors.append(trans.RevealHelpTransformer(config=config))
180 174 self.preprocessors.append(trans.CSSHtmlHeaderTransformer(config=config))
181 175 if SphinxTransformer:
182 176 self.preprocessors.append(SphinxTransformer(config=config))
183 177 self.preprocessors.append(LatexTransformer(config=config))
184 178
185 179 ##
186 180 self.env.filters['filter_data_type'] = FilterDataType(config=config)
187 181 self.env.filters['pycomment'] = python_comment
188 182 self.env.filters['indent'] = indent
189 183 self.env.filters['rm_fake'] = rm_fake
190 184 self.env.filters['rm_ansi'] = remove_ansi
191 185 self.env.filters['markdown'] = markdown
192 186 self.env.filters['highlight'] = highlight2latex if self.tex_environement else highlight
193 187 self.env.filters['highlight2html'] = highlight
194 188 self.env.filters['highlight2latex'] = highlight2latex
195 189 self.env.filters['ansi2html'] = ansi2html
196 190 self.env.filters['markdown2latex'] = markdown2latex
197 191 self.env.filters['markdown2rst'] = markdown2rst
198 192 self.env.filters['get_lines'] = get_lines
199 193 self.env.filters['wrap'] = wrap
200 194 self.env.filters['rm_dollars'] = rm_dollars
201 195 self.env.filters['rm_math_space'] = rm_math_space
202 196
203 197 ## user filter will overwrite
204 198 for key, filtr in jinja_filters.iteritems():
205 199 if isinstance(filtr, MetaHasTraits):
206 200 self.env.filters[key] = filtr(config=config)
207 201 else :
208 202 self.env.filters[key] = filtr
209 203
210 204 self.template = self.env.get_template(self.template_file+self.ext)
211 205
212 206
213 207 def process(self, nb):
214 208 """
215 209 preprocess the notebook json for easier use with the templates.
216 210 will call all the `preprocessor`s in order before returning it.
217 211 """
218 212
219 213 # dict of 'resources' that could be made by the preprocessors
220 214 # like key/value data to extract files from ipynb like in latex conversion
221 215 resources = {}
222 216
223 217 for preprocessor in self.preprocessors:
224 218 nb, resources = preprocessor(nb, resources)
225 219
226 220 return nb, resources
227 221
228 222 def convert(self, nb):
229 223 """ convert the ipynb file
230 224
231 225 return both the converted ipynb file and a dict containing potential
232 226 other resources
233 227 """
234 228 nb, resources = self.process(nb)
235 229 return self.template.render(nb=nb, resources=resources), resources
236 230
237 231
238 232 def from_filename(self, filename):
239 233 """read and convert a notebook from a file name"""
240 234 with io.open(filename) as f:
241 235 return self.convert(nbformat.read(f, 'json'))
242 236
243 237 def from_file(self, filelike):
244 238 """read and convert a notebook from a filelike object
245 239
246 240 filelike object will just be "read" and should be json format..
247 241 """
248 242 return self.convert(nbformat.read(filelike, 'json'))
249 243
250 244 def from_json(self, json):
251 245 """ not implemented
252 246
253 247 Should convert from a json object
254 248 """
255 249 raise NotImplementedError('not implemented (yet?)')
@@ -1,183 +1,201 b''
1 1 #!/usr/bin/env python
2 2
3 3 """NBConvert is a utility for conversion of IPYNB files.
4 4
5 5 Commandline interface for the NBConvert conversion utility. Read the
6 6 readme.rst for usage information
7 7 """
8 8 #-----------------------------------------------------------------------------
9 # Copyright (c) 2013, the IPython Development Team.
9 #Copyright (c) 2013, the IPython Development Team.
10 10 #
11 # Distributed under the terms of the Modified BSD License.
11 #Distributed under the terms of the Modified BSD License.
12 12 #
13 # The full license is in the file COPYING.txt, distributed with this software.
13 #The full license is in the file COPYING.txt, distributed with this software.
14 14 #-----------------------------------------------------------------------------
15 15
16 16 #-----------------------------------------------------------------------------
17 # Imports
17 #Imports
18 18 #-----------------------------------------------------------------------------
19 19
20 # Stdlib imports
20 #Stdlib imports
21 21 from __future__ import print_function
22 22 import sys
23 23 import io
24 24 import os
25 25
26 # From IPython
27 # All the stuff needed for the configurable things
26 #From IPython
27 #All the stuff needed for the configurable things
28 28 from IPython.config.application import Application
29 29 from IPython.config.loader import ConfigFileNotFound
30 30 from IPython.utils.traitlets import Unicode, Bool
31 31
32 # Local imports
33 from converters.template import ConverterTemplate #TODO
32 #Local imports
33 from api.exporter import Exporter
34 34 from converters.config import GlobalConfigurable #TODO
35 35 from converters.transformers import (ExtractFigureTransformer) #TODO
36 36
37 37 #-----------------------------------------------------------------------------
38 # Globals and constants
38 #Globals and constants
39 39 #-----------------------------------------------------------------------------
40 40 NBCONVERT_DIR = os.path.abspath(os.path.realpath(os.path.dirname(__file__)))
41
42 #'Keys in resources' user prompt.
43 KEYS_PROMPT_HEAD = "====================== Keys in Resources =================================="
44 KEYS_PROMPT_BODY = """
45 ===========================================================================
46 You are responsible for writting these files into the appropriate
47 directorie(s) if need be. If you do not want to see this message, enable
48 the 'write' (boolean) flag of the converter.
49 ===========================================================================
50 """
51
52 #Error Messages
41 53 ERROR_CONFIG_NOT_FOUND = "Config file for profile '%s' not found, giving up."
54
42 55 #-----------------------------------------------------------------------------
43 # Classes and functions
56 #Classes and functions
44 57 #-----------------------------------------------------------------------------
45 58 class NbconvertApp(Application):
46 59 """A basic application to convert ipynb files"""
47 60
48 61 stdout = Bool(
49 62 True, config=True,
50 63 help="""Whether to print the converted ipynb file to stdout
51 64 "use full do diff files without actually writing a new file"""
52 65 )
53 66
54 67 write = Bool(
55 68 False, config=True,
56 69 help="""Should the converted notebook file be written to disk
57 70 along with potential extracted resources."""
58 71 )
59 72
60 73 fileext = Unicode(
61 74 'txt', config=True,
62 75 help="Extension of the file that should be written to disk"
63 76 )
64 77
65 78 aliases = {
66 79 'stdout':'NbconvertApp.stdout',
67 80 'write':'NbconvertApp.write'
68 81 }
69 82
70 83 flags = {}
71 84 flags['no-stdout'] = (
72 85 {'NbconvertApp' : {'stdout' : False}},
73 86 """Do not print converted file to stdout, equivalent to
74 87 --stdout=False"""
75 88 )
76 89
77 90 def __init__(self, **kwargs):
78 91 """Public constructor"""
79 92
80 93 #Call base class
81 94 super(NbconvertApp, self).__init__(**kwargs)
82 95
83 96 #Register class here to have help with help all
84 97 self.classes.insert(0, ConverterTemplate)
85 98 self.classes.insert(0, ExtractFigureTransformer)
86 99 self.classes.insert(0, GlobalConfigurable)
87 100
88 101
89 102 def load_config_file(self, profile_name):
90 103 """Load a config file from the config file dir
91 104
92 105 profile_name : {string} name of the profile file to load without file
93 106 extension.
94 107 """
95 108
96 109 #Try to load the config file. If the file isn't found, catch the
97 110 #exception.
98 111 try:
99 112 Application.load_config_file(
100 113 self,
101 114 profile_name + '.py',
102 115 path=[os.path.join(NBCONVERT_DIR, 'profile')]
103 116 )
104 117 return True
105 118
106 119 except ConfigFileNotFound:
107 120 self.log.warn(ERROR_CONFIG_NOT_FOUND, profile_name)
108 121 return False
109 122
110 123
111 124 def start(self, argv=None):
112 125 """Convert a notebook in one step"""
113 126
114 127 #Parse the commandline options.
115 128 self.parse_command_line(argv)
116 129
117 130 #Load an addition config file if specified by the user via the
118 131 #commandline.
119 132 cl_config = self.config
120 133 profile_file = argv[1]
121 134 if not self.load_config_file(profile_file):
122 135 exit(1)
123 136 self.update_config(cl_config)
124 137
125 138 #Call base
126 139 super(NbconvertApp, self).start()
127 140
128 141 #The last arguments in chain of arguments will be used as conversion
129 142 ipynb_file = (self.extra_args or [None])[2]
130 143
131 # If you are writting a custom transformer, append it to the dictionary
132 # below.
144 #If you are writting a custom transformer, append it to the dictionary
145 #below.
133 146 userpreprocessors = {}
134 147
135 # Create the Jinja template converter TODO, refactor this
136 C = ConverterTemplate(config=self.config, preprocessors=userpreprocessors)
148 #Create the Jinja template exporter. TODO: Add ability to
149 #import in IPYNB aswell
150 exporter = Exporter(config=self.config, preprocessors=userpreprocessors)
137 151
138 output, resources = C.from_filename(ipynb_file)
152 #Export
153 output, resources = exporter.from_filename(ipynb_file)
139 154 if self.stdout :
140 155 print(output.encode('utf-8'))
141 156
157 #Get the file name without the '.ipynb' (6 chars) extension and then
158 #remove any addition periods and spaces. The resulting name will
159 #be used to create the directory that the files will be exported
160 #into.
142 161 out_root = ipynb_file[:-6].replace('.', '_').replace(' ', '_')
143 162
163 #Write file output from conversion.
144 164 if self.write :
145 165 with io.open(os.path.join(out_root+'.'+self.fileext), 'w') as f:
146 166 f.write(output)
147 167
168 #Output any associate figures into the same "root" directory.
148 169 binkeys = resources.get('figures', {}).get('binary',{}).keys()
149 170 textkeys = resources.get('figures', {}).get('text',{}).keys()
150 171 if binkeys or textkeys :
151 172 if self.write:
152 173 files_dir = out_root+'_files'
153 174 if not os.path.exists(out_root+'_files'):
154 175 os.mkdir(files_dir)
155 176 for key in binkeys:
156 177 with io.open(os.path.join(files_dir, key), 'wb') as f:
157 178 f.write(resources['figures']['binary'][key])
158 179 for key in textkeys:
159 180 with io.open(os.path.join(files_dir, key), 'w') as f:
160 181 f.write(resources['figures']['text'][key])
161 182
183 #Figures that weren't exported which will need to be created by the
184 #user. Tell the user what figures these are.
162 185 elif self.stdout:
163 print('''====================== Keys in Resources ==================================''')
186 print(KEYS_PROMPT_HEAD)
164 187 print(resources['figures'].keys())
165 print("""
166 ===========================================================================
167 you are responsible from writing those data do a file in the right place if
168 they need to be.
169 ===========================================================================
170 """)
188 print(KEYS_PROMPT_BODY)
171 189
172 190 #-----------------------------------------------------------------------------
173 # Script main
191 #Script main
174 192 #-----------------------------------------------------------------------------
175 193 def main():
176 194 """Convert a notebook in one step"""
177
195
178 196 app = NbconvertApp.instance()
179 197 app.description = __doc__
180 198 app.run(argv=sys.argv)
181 199
182 200 if __name__ == '__main__':
183 201 main() No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now