##// END OF EJS Templates
Function -> variable
Jonathan Frederic -
Show More
@@ -1,518 +1,518 b''
1 1 """This module defines Exporter, a highly configurable converter
2 2 that uses Jinja2 to export notebook files into different formats.
3 3 """
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 from __future__ import print_function, absolute_import
18 18
19 19 # Stdlib imports
20 20 import io
21 21 import os
22 22 import inspect
23 23 import copy
24 24 import collections
25 25 import datetime
26 26
27 27 # other libs/dependencies
28 28 from jinja2 import Environment, FileSystemLoader, ChoiceLoader, TemplateNotFound
29 29
30 30 # IPython imports
31 31 from IPython.config.configurable import LoggingConfigurable
32 32 from IPython.config import Config
33 33 from IPython.nbformat import current as nbformat
34 34 from IPython.utils.traitlets import MetaHasTraits, DottedObjectName, Unicode, List, Dict, Any
35 35 from IPython.utils.importstring import import_item
36 from IPython.utils.text import indent, format_date
36 from IPython.utils import text
37 37 from IPython.utils import py3compat
38 38
39 39 from IPython.nbconvert import preprocessors as nbpreprocessors
40 40 from IPython.nbconvert import filters
41 41
42 42 #-----------------------------------------------------------------------------
43 43 # Globals and constants
44 44 #-----------------------------------------------------------------------------
45 45
46 46 #Jinja2 extensions to load.
47 47 JINJA_EXTENSIONS = ['jinja2.ext.loopcontrols']
48 48
49 49 default_filters = {
50 'indent': indent,
50 'indent': text.indent,
51 51 'markdown2html': filters.markdown2html,
52 52 'ansi2html': filters.ansi2html,
53 53 'filter_data_type': filters.DataTypeFilter,
54 54 'get_lines': filters.get_lines,
55 55 'highlight2html': filters.highlight2html,
56 56 'highlight2latex': filters.highlight2latex,
57 57 'ipython2python': filters.ipython2python,
58 58 'posix_path': filters.posix_path,
59 59 'markdown2latex': filters.markdown2latex,
60 60 'markdown2rst': filters.markdown2rst,
61 61 'comment_lines': filters.comment_lines,
62 62 'strip_ansi': filters.strip_ansi,
63 63 'strip_dollars': filters.strip_dollars,
64 64 'strip_files_prefix': filters.strip_files_prefix,
65 65 'html2text' : filters.html2text,
66 66 'add_anchor': filters.add_anchor,
67 67 'ansi2latex': filters.ansi2latex,
68 68 'strip_math_space': filters.strip_math_space,
69 69 'wrap_text': filters.wrap_text,
70 70 'escape_latex': filters.escape_latex,
71 71 }
72 72
73 73 #-----------------------------------------------------------------------------
74 74 # Class
75 75 #-----------------------------------------------------------------------------
76 76
77 77 class ResourcesDict(collections.defaultdict):
78 78 def __missing__(self, key):
79 79 return ''
80 80
81 81
82 82 class Exporter(LoggingConfigurable):
83 83 """
84 84 Exports notebooks into other file formats. Uses Jinja 2 templating engine
85 85 to output new formats. Inherit from this class if you are creating a new
86 86 template type along with new filters/preprocessors. If the filters/
87 87 preprocessors provided by default suffice, there is no need to inherit from
88 88 this class. Instead, override the template_file and file_extension
89 89 traits via a config file.
90 90
91 91 {filters}
92 92 """
93 93
94 94 # finish the docstring
95 95 __doc__ = __doc__.format(filters = '- '+'\n - '.join(default_filters.keys()))
96 96
97 97
98 98 template_file = Unicode(u'default',
99 99 config=True,
100 100 help="Name of the template file to use")
101 101 def _template_file_changed(self, name, old, new):
102 102 if new=='default':
103 103 self.template_file = self.default_template
104 104 else:
105 105 self.template_file = new
106 106 self.template = None
107 107 self._load_template()
108 108
109 109 default_template = Unicode(u'')
110 110 template = Any()
111 111 environment = Any()
112 112
113 113 file_extension = Unicode(
114 114 'txt', config=True,
115 115 help="Extension of the file that should be written to disk"
116 116 )
117 117
118 118 template_path = List(['.'], config=True)
119 119 def _template_path_changed(self, name, old, new):
120 120 self._load_template()
121 121
122 122 default_template_path = Unicode(
123 123 os.path.join("..", "templates"),
124 124 help="Path where the template files are located.")
125 125
126 126 template_skeleton_path = Unicode(
127 127 os.path.join("..", "templates", "skeleton"),
128 128 help="Path where the template skeleton files are located.")
129 129
130 130 #Jinja block definitions
131 131 jinja_comment_block_start = Unicode("", config=True)
132 132 jinja_comment_block_end = Unicode("", config=True)
133 133 jinja_variable_block_start = Unicode("", config=True)
134 134 jinja_variable_block_end = Unicode("", config=True)
135 135 jinja_logic_block_start = Unicode("", config=True)
136 136 jinja_logic_block_end = Unicode("", config=True)
137 137
138 138 #Extension that the template files use.
139 139 template_extension = Unicode(".tpl", config=True)
140 140
141 141 #Configurability, allows the user to easily add filters and preprocessors.
142 142 preprocessors = List(config=True,
143 143 help="""List of preprocessors, by name or namespace, to enable.""")
144 144
145 145 filters = Dict(config=True,
146 146 help="""Dictionary of filters, by name and namespace, to add to the Jinja
147 147 environment.""")
148 148
149 149 default_preprocessors = List([nbpreprocessors.coalesce_streams,
150 150 nbpreprocessors.SVG2PDFPreprocessor,
151 151 nbpreprocessors.ExtractOutputPreprocessor,
152 152 nbpreprocessors.CSSHTMLHeaderPreprocessor,
153 153 nbpreprocessors.RevealHelpPreprocessor,
154 154 nbpreprocessors.LatexPreprocessor,
155 155 nbpreprocessors.SphinxPreprocessor],
156 156 config=True,
157 157 help="""List of preprocessors available by default, by name, namespace,
158 158 instance, or type.""")
159 159
160 160
161 161 def __init__(self, config=None, extra_loaders=None, **kw):
162 162 """
163 163 Public constructor
164 164
165 165 Parameters
166 166 ----------
167 167 config : config
168 168 User configuration instance.
169 169 extra_loaders : list[of Jinja Loaders]
170 170 ordered list of Jinja loader to find templates. Will be tried in order
171 171 before the default FileSystem ones.
172 172 template : str (optional, kw arg)
173 173 Template to use when exporting.
174 174 """
175 175 if not config:
176 176 config = self.default_config
177 177
178 178 super(Exporter, self).__init__(config=config, **kw)
179 179
180 180 #Init
181 181 self._init_template()
182 182 self._init_environment(extra_loaders=extra_loaders)
183 183 self._init_preprocessors()
184 184 self._init_filters()
185 185
186 186
187 187 @property
188 188 def default_config(self):
189 189 return Config()
190 190
191 191 def _config_changed(self, name, old, new):
192 192 """When setting config, make sure to start with our default_config"""
193 193 c = self.default_config
194 194 if new:
195 195 c.merge(new)
196 196 if c != old:
197 197 self.config = c
198 198 super(Exporter, self)._config_changed(name, old, c)
199 199
200 200
201 201 def _load_template(self):
202 202 """Load the Jinja template object from the template file
203 203
204 204 This is a no-op if the template attribute is already defined,
205 205 or the Jinja environment is not setup yet.
206 206
207 207 This is triggered by various trait changes that would change the template.
208 208 """
209 209 if self.template is not None:
210 210 return
211 211 # called too early, do nothing
212 212 if self.environment is None:
213 213 return
214 214 # Try different template names during conversion. First try to load the
215 215 # template by name with extension added, then try loading the template
216 216 # as if the name is explicitly specified, then try the name as a
217 217 # 'flavor', and lastly just try to load the template by module name.
218 218 module_name = self.__module__.rsplit('.', 1)[-1]
219 219 try_names = []
220 220 if self.template_file:
221 221 try_names.extend([
222 222 self.template_file + self.template_extension,
223 223 self.template_file,
224 224 module_name + '_' + self.template_file + self.template_extension,
225 225 ])
226 226 try_names.append(module_name + self.template_extension)
227 227 for try_name in try_names:
228 228 self.log.debug("Attempting to load template %s", try_name)
229 229 try:
230 230 self.template = self.environment.get_template(try_name)
231 231 except (TemplateNotFound, IOError):
232 232 pass
233 233 except Exception as e:
234 234 self.log.warn("Unexpected exception loading template: %s", try_name, exc_info=True)
235 235 else:
236 236 self.log.info("Loaded template %s", try_name)
237 237 break
238 238
239 239 def from_notebook_node(self, nb, resources=None, **kw):
240 240 """
241 241 Convert a notebook from a notebook node instance.
242 242
243 243 Parameters
244 244 ----------
245 245 nb : Notebook node
246 246 resources : dict (**kw)
247 247 of additional resources that can be accessed read/write by
248 248 preprocessors and filters.
249 249 """
250 250 nb_copy = copy.deepcopy(nb)
251 251 resources = self._init_resources(resources)
252 252
253 253 # Preprocess
254 254 nb_copy, resources = self._preprocess(nb_copy, resources)
255 255
256 256 self._load_template()
257 257
258 258 if self.template is not None:
259 259 output = self.template.render(nb=nb_copy, resources=resources)
260 260 else:
261 261 raise IOError('template file "%s" could not be found' % self.template_file)
262 262 return output, resources
263 263
264 264
265 265 def from_filename(self, filename, resources=None, **kw):
266 266 """
267 267 Convert a notebook from a notebook file.
268 268
269 269 Parameters
270 270 ----------
271 271 filename : str
272 272 Full filename of the notebook file to open and convert.
273 273 """
274 274
275 275 #Pull the metadata from the filesystem.
276 276 if resources is None:
277 277 resources = ResourcesDict()
278 278 if not 'metadata' in resources or resources['metadata'] == '':
279 279 resources['metadata'] = ResourcesDict()
280 280 basename = os.path.basename(filename)
281 281 notebook_name = basename[:basename.rfind('.')]
282 282 resources['metadata']['name'] = notebook_name
283 283
284 284 modified_date = datetime.datetime.fromtimestamp(os.path.getmtime(filename))
285 resources['metadata']['modified_date'] = format_date(modified_date)
285 resources['metadata']['modified_date'] = modified_date.strftime(text.date_format)
286 286
287 287 with io.open(filename) as f:
288 288 return self.from_notebook_node(nbformat.read(f, 'json'), resources=resources,**kw)
289 289
290 290
291 291 def from_file(self, file_stream, resources=None, **kw):
292 292 """
293 293 Convert a notebook from a notebook file.
294 294
295 295 Parameters
296 296 ----------
297 297 file_stream : file-like object
298 298 Notebook file-like object to convert.
299 299 """
300 300 return self.from_notebook_node(nbformat.read(file_stream, 'json'), resources=resources, **kw)
301 301
302 302
303 303 def register_preprocessor(self, preprocessor, enabled=False):
304 304 """
305 305 Register a preprocessor.
306 306 Preprocessors are classes that act upon the notebook before it is
307 307 passed into the Jinja templating engine. Preprocessors are also
308 308 capable of passing additional information to the Jinja
309 309 templating engine.
310 310
311 311 Parameters
312 312 ----------
313 313 preprocessor : preprocessor
314 314 """
315 315 if preprocessor is None:
316 316 raise TypeError('preprocessor')
317 317 isclass = isinstance(preprocessor, type)
318 318 constructed = not isclass
319 319
320 320 #Handle preprocessor's registration based on it's type
321 321 if constructed and isinstance(preprocessor, py3compat.string_types):
322 322 #Preprocessor is a string, import the namespace and recursively call
323 323 #this register_preprocessor method
324 324 preprocessor_cls = import_item(preprocessor)
325 325 return self.register_preprocessor(preprocessor_cls, enabled)
326 326
327 327 if constructed and hasattr(preprocessor, '__call__'):
328 328 #Preprocessor is a function, no need to construct it.
329 329 #Register and return the preprocessor.
330 330 if enabled:
331 331 preprocessor.enabled = True
332 332 self._preprocessors.append(preprocessor)
333 333 return preprocessor
334 334
335 335 elif isclass and isinstance(preprocessor, MetaHasTraits):
336 336 #Preprocessor is configurable. Make sure to pass in new default for
337 337 #the enabled flag if one was specified.
338 338 self.register_preprocessor(preprocessor(parent=self), enabled)
339 339
340 340 elif isclass:
341 341 #Preprocessor is not configurable, construct it
342 342 self.register_preprocessor(preprocessor(), enabled)
343 343
344 344 else:
345 345 #Preprocessor is an instance of something without a __call__
346 346 #attribute.
347 347 raise TypeError('preprocessor')
348 348
349 349
350 350 def register_filter(self, name, jinja_filter):
351 351 """
352 352 Register a filter.
353 353 A filter is a function that accepts and acts on one string.
354 354 The filters are accesible within the Jinja templating engine.
355 355
356 356 Parameters
357 357 ----------
358 358 name : str
359 359 name to give the filter in the Jinja engine
360 360 filter : filter
361 361 """
362 362 if jinja_filter is None:
363 363 raise TypeError('filter')
364 364 isclass = isinstance(jinja_filter, type)
365 365 constructed = not isclass
366 366
367 367 #Handle filter's registration based on it's type
368 368 if constructed and isinstance(jinja_filter, py3compat.string_types):
369 369 #filter is a string, import the namespace and recursively call
370 370 #this register_filter method
371 371 filter_cls = import_item(jinja_filter)
372 372 return self.register_filter(name, filter_cls)
373 373
374 374 if constructed and hasattr(jinja_filter, '__call__'):
375 375 #filter is a function, no need to construct it.
376 376 self.environment.filters[name] = jinja_filter
377 377 return jinja_filter
378 378
379 379 elif isclass and isinstance(jinja_filter, MetaHasTraits):
380 380 #filter is configurable. Make sure to pass in new default for
381 381 #the enabled flag if one was specified.
382 382 filter_instance = jinja_filter(parent=self)
383 383 self.register_filter(name, filter_instance )
384 384
385 385 elif isclass:
386 386 #filter is not configurable, construct it
387 387 filter_instance = jinja_filter()
388 388 self.register_filter(name, filter_instance)
389 389
390 390 else:
391 391 #filter is an instance of something without a __call__
392 392 #attribute.
393 393 raise TypeError('filter')
394 394
395 395
396 396 def _init_template(self):
397 397 """
398 398 Make sure a template name is specified. If one isn't specified, try to
399 399 build one from the information we know.
400 400 """
401 401 self._template_file_changed('template_file', self.template_file, self.template_file)
402 402
403 403
404 404 def _init_environment(self, extra_loaders=None):
405 405 """
406 406 Create the Jinja templating environment.
407 407 """
408 408 here = os.path.dirname(os.path.realpath(__file__))
409 409 loaders = []
410 410 if extra_loaders:
411 411 loaders.extend(extra_loaders)
412 412
413 413 paths = self.template_path
414 414 paths.extend([os.path.join(here, self.default_template_path),
415 415 os.path.join(here, self.template_skeleton_path)])
416 416 loaders.append(FileSystemLoader(paths))
417 417
418 418 self.environment = Environment(
419 419 loader= ChoiceLoader(loaders),
420 420 extensions=JINJA_EXTENSIONS
421 421 )
422 422
423 423 #Set special Jinja2 syntax that will not conflict with latex.
424 424 if self.jinja_logic_block_start:
425 425 self.environment.block_start_string = self.jinja_logic_block_start
426 426 if self.jinja_logic_block_end:
427 427 self.environment.block_end_string = self.jinja_logic_block_end
428 428 if self.jinja_variable_block_start:
429 429 self.environment.variable_start_string = self.jinja_variable_block_start
430 430 if self.jinja_variable_block_end:
431 431 self.environment.variable_end_string = self.jinja_variable_block_end
432 432 if self.jinja_comment_block_start:
433 433 self.environment.comment_start_string = self.jinja_comment_block_start
434 434 if self.jinja_comment_block_end:
435 435 self.environment.comment_end_string = self.jinja_comment_block_end
436 436
437 437
438 438 def _init_preprocessors(self):
439 439 """
440 440 Register all of the preprocessors needed for this exporter, disabled
441 441 unless specified explicitly.
442 442 """
443 443 self._preprocessors = []
444 444
445 445 #Load default preprocessors (not necessarly enabled by default).
446 446 if self.default_preprocessors:
447 447 for preprocessor in self.default_preprocessors:
448 448 self.register_preprocessor(preprocessor)
449 449
450 450 #Load user preprocessors. Enable by default.
451 451 if self.preprocessors:
452 452 for preprocessor in self.preprocessors:
453 453 self.register_preprocessor(preprocessor, enabled=True)
454 454
455 455
456 456 def _init_filters(self):
457 457 """
458 458 Register all of the filters required for the exporter.
459 459 """
460 460
461 461 #Add default filters to the Jinja2 environment
462 462 for key, value in default_filters.items():
463 463 self.register_filter(key, value)
464 464
465 465 #Load user filters. Overwrite existing filters if need be.
466 466 if self.filters:
467 467 for key, user_filter in self.filters.items():
468 468 self.register_filter(key, user_filter)
469 469
470 470
471 471 def _init_resources(self, resources):
472 472
473 473 #Make sure the resources dict is of ResourcesDict type.
474 474 if resources is None:
475 475 resources = ResourcesDict()
476 476 if not isinstance(resources, ResourcesDict):
477 477 new_resources = ResourcesDict()
478 478 new_resources.update(resources)
479 479 resources = new_resources
480 480
481 481 #Make sure the metadata extension exists in resources
482 482 if 'metadata' in resources:
483 483 if not isinstance(resources['metadata'], ResourcesDict):
484 484 resources['metadata'] = ResourcesDict(resources['metadata'])
485 485 else:
486 486 resources['metadata'] = ResourcesDict()
487 487 if not resources['metadata']['name']:
488 488 resources['metadata']['name'] = 'Notebook'
489 489
490 490 #Set the output extension
491 491 resources['output_extension'] = self.file_extension
492 492 return resources
493 493
494 494
495 495 def _preprocess(self, nb, resources):
496 496 """
497 497 Preprocess the notebook before passing it into the Jinja engine.
498 498 To preprocess the notebook is to apply all of the
499 499
500 500 Parameters
501 501 ----------
502 502 nb : notebook node
503 503 notebook that is being exported.
504 504 resources : a dict of additional resources that
505 505 can be accessed read/write by preprocessors
506 506 and filters.
507 507 """
508 508
509 509 # Do a copy.deepcopy first,
510 510 # we are never safe enough with what the preprocessors could do.
511 511 nbc = copy.deepcopy(nb)
512 512 resc = copy.deepcopy(resources)
513 513
514 514 #Run each preprocessor on the notebook. Carry the output along
515 515 #to each preprocessor
516 516 for preprocessor in self._preprocessors:
517 517 nbc, resc = preprocessor(nbc, resc)
518 518 return nbc, resc
@@ -1,265 +1,265 b''
1 1 """Module that allows custom Sphinx parameters to be set on the notebook and
2 2 on the 'other' object passed into Jinja. Called prior to Jinja conversion
3 3 process.
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 from __future__ import print_function, absolute_import
18 18
19 19 # Stdlib imports
20 20 import os.path
21 21 import sys
22 22
23 23 # Used to set the default date to today's date
24 24 from datetime import date
25 25
26 26 # Third-party imports
27 27 # Needed for Pygments latex definitions.
28 28 from pygments.formatters import LatexFormatter
29 29
30 30 # Our own imports
31 31 # Configurable traitlets
32 32 from IPython.utils.traitlets import Unicode, Bool
33 from IPython.utils.text import format_date
33 from IPython.utils import text
34 34
35 35 # Needed to override preprocessor
36 36 from .base import (Preprocessor)
37 37
38 38 from IPython.nbconvert.utils import console
39 39
40 40 #-----------------------------------------------------------------------------
41 41 # Classes and functions
42 42 #-----------------------------------------------------------------------------
43 43
44 44 class SphinxPreprocessor(Preprocessor):
45 45 """
46 46 Sphinx utility preprocessor.
47 47
48 48 This preprocessor is used to set variables needed by the latex to build
49 49 Sphinx stylized templates.
50 50 """
51 51
52 52 interactive = Bool(False, config=True, help="""
53 53 Allows you to define whether or not the Sphinx exporter will prompt
54 54 you for input during the conversion process. If this is set to false,
55 55 the author, version, release, date, and chapter_style traits should
56 56 be set.
57 57 """)
58 58
59 59 author = Unicode("Unknown Author", config=True, help="Author name")
60 60
61 61 version = Unicode("", config=True, help="""
62 62 Version number
63 63 You can leave this blank if you do not want to render a version number.
64 64 Example: "1.0.0"
65 65 """)
66 66
67 67 release = Unicode("", config=True, help="""
68 68 Release name
69 69 You can leave this blank if you do not want to render a release name.
70 70 Example: "Rough Draft"
71 71 """)
72 72
73 73 publish_date = Unicode("", config=True, help="""
74 74 Publish date
75 75 This is the date to render on the document as the publish date.
76 76 Leave this blank to default to todays date.
77 77 Example: "June 12, 1990"
78 78 """)
79 79
80 80 chapter_style = Unicode("Bjarne", config=True, help="""
81 81 Sphinx chapter style
82 82 This is the style to use for the chapter headers in the document.
83 83 You may choose one of the following:
84 84 "Bjarne" (default)
85 85 "Lenny"
86 86 "Glenn"
87 87 "Conny"
88 88 "Rejne"
89 89 "Sonny" (used for international documents)
90 90 """)
91 91
92 92 output_style = Unicode("notebook", config=True, help="""
93 93 Nbconvert Ipython
94 94 notebook input/output formatting style.
95 95 You may choose one of the following:
96 96 "simple (recommended for long code segments)"
97 97 "notebook" (default)
98 98 """)
99 99
100 100 center_output = Bool(False, config=True, help="""
101 101 Optional attempt to center all output. If this is false, no additional
102 102 formatting is applied.
103 103 """)
104 104
105 105 use_headers = Bool(True, config=True, help="""
106 106 Whether not a header should be added to the document.
107 107 """)
108 108
109 109 #Allow the user to override the title of the notebook (useful for
110 110 #fancy document titles that the file system doesn't support.)
111 111 overridetitle = Unicode("", config=True, help="")
112 112
113 113
114 114 def preprocess(self, nb, resources):
115 115 """
116 116 Sphinx preprocessing to apply on each notebook.
117 117
118 118 Parameters
119 119 ----------
120 120 nb : NotebookNode
121 121 Notebook being converted
122 122 resources : dictionary
123 123 Additional resources used in the conversion process. Allows
124 124 preprocessors to pass variables into the Jinja engine.
125 125 """
126 126 # import sphinx here, so that sphinx is not a dependency when it's not used
127 127 import sphinx
128 128
129 129 # TODO: Add versatile method of additional notebook metadata. Include
130 130 # handling of multiple files. For now use a temporay namespace,
131 131 # '_draft' to signify that this needs to change.
132 132 if not isinstance(resources["sphinx"], dict):
133 133 resources["sphinx"] = {}
134 134
135 135 if self.interactive:
136 136
137 137 # Prompt the user for additional meta data that doesn't exist currently
138 138 # but would be usefull for Sphinx.
139 139 resources["sphinx"]["author"] = self._prompt_author()
140 140 resources["sphinx"]["version"] = self._prompt_version()
141 141 resources["sphinx"]["release"] = self._prompt_release()
142 142 resources["sphinx"]["date"] = self._prompt_date()
143 143
144 144 # Prompt the user for the document style.
145 145 resources["sphinx"]["chapterstyle"] = self._prompt_chapter_title_style()
146 146 resources["sphinx"]["outputstyle"] = self._prompt_output_style()
147 147
148 148 # Small options
149 149 resources["sphinx"]["centeroutput"] = console.prompt_boolean("Do you want to center the output? (false)", False)
150 150 resources["sphinx"]["header"] = console.prompt_boolean("Should a Sphinx document header be used? (true)", True)
151 151 else:
152 152
153 153 # Try to use the traitlets.
154 154 resources["sphinx"]["author"] = self.author
155 155 resources["sphinx"]["version"] = self.version
156 156 resources["sphinx"]["release"] = self.release
157 157
158 158 # Use todays date if none is provided.
159 159 if self.publish_date:
160 160 resources["sphinx"]["date"] = self.publish_date
161 161 elif len(resources['metadata']['modified_date'].strip()) == 0:
162 resources["sphinx"]["date"] = format_date(date.today())
162 resources["sphinx"]["date"] = date.today().strftime(text.date_format)
163 163 else:
164 164 resources["sphinx"]["date"] = resources['metadata']['modified_date']
165 165
166 166 # Sphinx traitlets.
167 167 resources["sphinx"]["chapterstyle"] = self.chapter_style
168 168 resources["sphinx"]["outputstyle"] = self.output_style
169 169 resources["sphinx"]["centeroutput"] = self.center_output
170 170 resources["sphinx"]["header"] = self.use_headers
171 171
172 172 # Find and pass in the path to the Sphinx dependencies.
173 173 resources["sphinx"]["texinputs"] = os.path.realpath(os.path.join(sphinx.package_dir, "texinputs"))
174 174
175 175 # Generate Pygments definitions for Latex
176 176 resources["sphinx"]["pygment_definitions"] = self._generate_pygments_latex_def()
177 177
178 178 if not (self.overridetitle == None or len(self.overridetitle.strip()) == 0):
179 179 resources['metadata']['name'] = self.overridetitle
180 180
181 181 # End
182 182 return nb, resources
183 183
184 184
185 185 def _generate_pygments_latex_def(self):
186 186 """
187 187 Generate the pygments latex definitions that allows pygments
188 188 to work in latex.
189 189 """
190 190
191 191 return LatexFormatter().get_style_defs()
192 192
193 193
194 194 def _prompt_author(self):
195 195 """
196 196 Prompt the user to input an Author name
197 197 """
198 198 return console.input("Author name: ")
199 199
200 200
201 201 def _prompt_version(self):
202 202 """
203 203 prompt the user to enter a version number
204 204 """
205 205 return console.input("Version (ie ""1.0.0""): ")
206 206
207 207
208 208 def _prompt_release(self):
209 209 """
210 210 Prompt the user to input a release name
211 211 """
212 212
213 213 return console.input("Release Name (ie ""Rough draft""): ")
214 214
215 215
216 216 def _prompt_date(self, resources):
217 217 """
218 218 Prompt the user to enter a date
219 219 """
220 220
221 221 if resources['metadata']['modified_date']:
222 222 default_date = resources['metadata']['modified_date']
223 223 else:
224 default_date = format_date(date.today())
224 default_date = date.today().strftime(text.date_format)
225 225
226 226 user_date = console.input("Date (deafults to \"" + default_date + "\"): ")
227 227 if len(user_date.strip()) == 0:
228 228 user_date = default_date
229 229 return user_date
230 230
231 231
232 232 def _prompt_output_style(self):
233 233 """
234 234 Prompts the user to pick an IPython output style.
235 235 """
236 236
237 237 # Dictionary of available output styles
238 238 styles = {1: "simple",
239 239 2: "notebook"}
240 240
241 241 #Append comments to the menu when displaying it to the user.
242 242 comments = {1: "(recommended for long code segments)",
243 243 2: "(default)"}
244 244
245 245 return console.prompt_dictionary(styles, default_style=2, menu_comments=comments)
246 246
247 247
248 248 def _prompt_chapter_title_style(self):
249 249 """
250 250 Prompts the user to pick a Sphinx chapter style
251 251 """
252 252
253 253 # Dictionary of available Sphinx styles
254 254 styles = {1: "Bjarne",
255 255 2: "Lenny",
256 256 3: "Glenn",
257 257 4: "Conny",
258 258 5: "Rejne",
259 259 6: "Sonny"}
260 260
261 261 #Append comments to the menu when displaying it to the user.
262 262 comments = {1: "(default)",
263 263 6: "(for international documents)"}
264 264
265 265 return console.prompt_dictionary(styles, menu_comments=comments)
@@ -1,732 +1,726 b''
1 1 # encoding: utf-8
2 2 """
3 3 Utilities for working with strings and text.
4 4
5 5 Inheritance diagram:
6 6
7 7 .. inheritance-diagram:: IPython.utils.text
8 8 :parts: 3
9 9 """
10 10
11 11 #-----------------------------------------------------------------------------
12 12 # Copyright (C) 2008-2011 The IPython Development Team
13 13 #
14 14 # Distributed under the terms of the BSD License. The full license is in
15 15 # the file COPYING, distributed as part of this software.
16 16 #-----------------------------------------------------------------------------
17 17
18 18 #-----------------------------------------------------------------------------
19 19 # Imports
20 20 #-----------------------------------------------------------------------------
21 21
22 22 import os
23 23 import re
24 24 import sys
25 25 import textwrap
26 26 from string import Formatter
27 27
28 28 from IPython.external.path import path
29 29 from IPython.testing.skipdoctest import skip_doctest_py3, skip_doctest
30 30 from IPython.utils import py3compat
31 31
32
33 #-----------------------------------------------------------------------------
34 # Declarations
35 #-----------------------------------------------------------------------------
36
37 # datetime.strftime date format for ipython
38 if sys.platform == 'win32':
39 date_format = date.strftime("%B %d, %Y")
40 else:
41 date_format = date.strftime("%B %-d, %Y")
42
43
32 44 #-----------------------------------------------------------------------------
33 45 # Code
34 46 #-----------------------------------------------------------------------------
35 47
36 48 class LSString(str):
37 49 """String derivative with a special access attributes.
38 50
39 51 These are normal strings, but with the special attributes:
40 52
41 53 .l (or .list) : value as list (split on newlines).
42 54 .n (or .nlstr): original value (the string itself).
43 55 .s (or .spstr): value as whitespace-separated string.
44 56 .p (or .paths): list of path objects
45 57
46 58 Any values which require transformations are computed only once and
47 59 cached.
48 60
49 61 Such strings are very useful to efficiently interact with the shell, which
50 62 typically only understands whitespace-separated options for commands."""
51 63
52 64 def get_list(self):
53 65 try:
54 66 return self.__list
55 67 except AttributeError:
56 68 self.__list = self.split('\n')
57 69 return self.__list
58 70
59 71 l = list = property(get_list)
60 72
61 73 def get_spstr(self):
62 74 try:
63 75 return self.__spstr
64 76 except AttributeError:
65 77 self.__spstr = self.replace('\n',' ')
66 78 return self.__spstr
67 79
68 80 s = spstr = property(get_spstr)
69 81
70 82 def get_nlstr(self):
71 83 return self
72 84
73 85 n = nlstr = property(get_nlstr)
74 86
75 87 def get_paths(self):
76 88 try:
77 89 return self.__paths
78 90 except AttributeError:
79 91 self.__paths = [path(p) for p in self.split('\n') if os.path.exists(p)]
80 92 return self.__paths
81 93
82 94 p = paths = property(get_paths)
83 95
84 96 # FIXME: We need to reimplement type specific displayhook and then add this
85 97 # back as a custom printer. This should also be moved outside utils into the
86 98 # core.
87 99
88 100 # def print_lsstring(arg):
89 101 # """ Prettier (non-repr-like) and more informative printer for LSString """
90 102 # print "LSString (.p, .n, .l, .s available). Value:"
91 103 # print arg
92 104 #
93 105 #
94 106 # print_lsstring = result_display.when_type(LSString)(print_lsstring)
95 107
96 108
97 109 class SList(list):
98 110 """List derivative with a special access attributes.
99 111
100 112 These are normal lists, but with the special attributes:
101 113
102 114 .l (or .list) : value as list (the list itself).
103 115 .n (or .nlstr): value as a string, joined on newlines.
104 116 .s (or .spstr): value as a string, joined on spaces.
105 117 .p (or .paths): list of path objects
106 118
107 119 Any values which require transformations are computed only once and
108 120 cached."""
109 121
110 122 def get_list(self):
111 123 return self
112 124
113 125 l = list = property(get_list)
114 126
115 127 def get_spstr(self):
116 128 try:
117 129 return self.__spstr
118 130 except AttributeError:
119 131 self.__spstr = ' '.join(self)
120 132 return self.__spstr
121 133
122 134 s = spstr = property(get_spstr)
123 135
124 136 def get_nlstr(self):
125 137 try:
126 138 return self.__nlstr
127 139 except AttributeError:
128 140 self.__nlstr = '\n'.join(self)
129 141 return self.__nlstr
130 142
131 143 n = nlstr = property(get_nlstr)
132 144
133 145 def get_paths(self):
134 146 try:
135 147 return self.__paths
136 148 except AttributeError:
137 149 self.__paths = [path(p) for p in self if os.path.exists(p)]
138 150 return self.__paths
139 151
140 152 p = paths = property(get_paths)
141 153
142 154 def grep(self, pattern, prune = False, field = None):
143 155 """ Return all strings matching 'pattern' (a regex or callable)
144 156
145 157 This is case-insensitive. If prune is true, return all items
146 158 NOT matching the pattern.
147 159
148 160 If field is specified, the match must occur in the specified
149 161 whitespace-separated field.
150 162
151 163 Examples::
152 164
153 165 a.grep( lambda x: x.startswith('C') )
154 166 a.grep('Cha.*log', prune=1)
155 167 a.grep('chm', field=-1)
156 168 """
157 169
158 170 def match_target(s):
159 171 if field is None:
160 172 return s
161 173 parts = s.split()
162 174 try:
163 175 tgt = parts[field]
164 176 return tgt
165 177 except IndexError:
166 178 return ""
167 179
168 180 if isinstance(pattern, basestring):
169 181 pred = lambda x : re.search(pattern, x, re.IGNORECASE)
170 182 else:
171 183 pred = pattern
172 184 if not prune:
173 185 return SList([el for el in self if pred(match_target(el))])
174 186 else:
175 187 return SList([el for el in self if not pred(match_target(el))])
176 188
177 189 def fields(self, *fields):
178 190 """ Collect whitespace-separated fields from string list
179 191
180 192 Allows quick awk-like usage of string lists.
181 193
182 194 Example data (in var a, created by 'a = !ls -l')::
183 195 -rwxrwxrwx 1 ville None 18 Dec 14 2006 ChangeLog
184 196 drwxrwxrwx+ 6 ville None 0 Oct 24 18:05 IPython
185 197
186 198 a.fields(0) is ['-rwxrwxrwx', 'drwxrwxrwx+']
187 199 a.fields(1,0) is ['1 -rwxrwxrwx', '6 drwxrwxrwx+']
188 200 (note the joining by space).
189 201 a.fields(-1) is ['ChangeLog', 'IPython']
190 202
191 203 IndexErrors are ignored.
192 204
193 205 Without args, fields() just split()'s the strings.
194 206 """
195 207 if len(fields) == 0:
196 208 return [el.split() for el in self]
197 209
198 210 res = SList()
199 211 for el in [f.split() for f in self]:
200 212 lineparts = []
201 213
202 214 for fd in fields:
203 215 try:
204 216 lineparts.append(el[fd])
205 217 except IndexError:
206 218 pass
207 219 if lineparts:
208 220 res.append(" ".join(lineparts))
209 221
210 222 return res
211 223
212 224 def sort(self,field= None, nums = False):
213 225 """ sort by specified fields (see fields())
214 226
215 227 Example::
216 228 a.sort(1, nums = True)
217 229
218 230 Sorts a by second field, in numerical order (so that 21 > 3)
219 231
220 232 """
221 233
222 234 #decorate, sort, undecorate
223 235 if field is not None:
224 236 dsu = [[SList([line]).fields(field), line] for line in self]
225 237 else:
226 238 dsu = [[line, line] for line in self]
227 239 if nums:
228 240 for i in range(len(dsu)):
229 241 numstr = "".join([ch for ch in dsu[i][0] if ch.isdigit()])
230 242 try:
231 243 n = int(numstr)
232 244 except ValueError:
233 245 n = 0;
234 246 dsu[i][0] = n
235 247
236 248
237 249 dsu.sort()
238 250 return SList([t[1] for t in dsu])
239 251
240 252
241 253 # FIXME: We need to reimplement type specific displayhook and then add this
242 254 # back as a custom printer. This should also be moved outside utils into the
243 255 # core.
244 256
245 257 # def print_slist(arg):
246 258 # """ Prettier (non-repr-like) and more informative printer for SList """
247 259 # print "SList (.p, .n, .l, .s, .grep(), .fields(), sort() available):"
248 260 # if hasattr(arg, 'hideonce') and arg.hideonce:
249 261 # arg.hideonce = False
250 262 # return
251 263 #
252 264 # nlprint(arg) # This was a nested list printer, now removed.
253 265 #
254 266 # print_slist = result_display.when_type(SList)(print_slist)
255 267
256 268
257 269 def indent(instr,nspaces=4, ntabs=0, flatten=False):
258 270 """Indent a string a given number of spaces or tabstops.
259 271
260 272 indent(str,nspaces=4,ntabs=0) -> indent str by ntabs+nspaces.
261 273
262 274 Parameters
263 275 ----------
264 276
265 277 instr : basestring
266 278 The string to be indented.
267 279 nspaces : int (default: 4)
268 280 The number of spaces to be indented.
269 281 ntabs : int (default: 0)
270 282 The number of tabs to be indented.
271 283 flatten : bool (default: False)
272 284 Whether to scrub existing indentation. If True, all lines will be
273 285 aligned to the same indentation. If False, existing indentation will
274 286 be strictly increased.
275 287
276 288 Returns
277 289 -------
278 290
279 291 str|unicode : string indented by ntabs and nspaces.
280 292
281 293 """
282 294 if instr is None:
283 295 return
284 296 ind = '\t'*ntabs+' '*nspaces
285 297 if flatten:
286 298 pat = re.compile(r'^\s*', re.MULTILINE)
287 299 else:
288 300 pat = re.compile(r'^', re.MULTILINE)
289 301 outstr = re.sub(pat, ind, instr)
290 302 if outstr.endswith(os.linesep+ind):
291 303 return outstr[:-len(ind)]
292 304 else:
293 305 return outstr
294 306
295 307
296 308 def list_strings(arg):
297 309 """Always return a list of strings, given a string or list of strings
298 310 as input.
299 311
300 312 :Examples:
301 313
302 314 In [7]: list_strings('A single string')
303 315 Out[7]: ['A single string']
304 316
305 317 In [8]: list_strings(['A single string in a list'])
306 318 Out[8]: ['A single string in a list']
307 319
308 320 In [9]: list_strings(['A','list','of','strings'])
309 321 Out[9]: ['A', 'list', 'of', 'strings']
310 322 """
311 323
312 324 if isinstance(arg,basestring): return [arg]
313 325 else: return arg
314 326
315 327
316 328 def marquee(txt='',width=78,mark='*'):
317 329 """Return the input string centered in a 'marquee'.
318 330
319 331 :Examples:
320 332
321 333 In [16]: marquee('A test',40)
322 334 Out[16]: '**************** A test ****************'
323 335
324 336 In [17]: marquee('A test',40,'-')
325 337 Out[17]: '---------------- A test ----------------'
326 338
327 339 In [18]: marquee('A test',40,' ')
328 340 Out[18]: ' A test '
329 341
330 342 """
331 343 if not txt:
332 344 return (mark*width)[:width]
333 345 nmark = (width-len(txt)-2)//len(mark)//2
334 346 if nmark < 0: nmark =0
335 347 marks = mark*nmark
336 348 return '%s %s %s' % (marks,txt,marks)
337 349
338 350
339 351 ini_spaces_re = re.compile(r'^(\s+)')
340 352
341 353 def num_ini_spaces(strng):
342 354 """Return the number of initial spaces in a string"""
343 355
344 356 ini_spaces = ini_spaces_re.match(strng)
345 357 if ini_spaces:
346 358 return ini_spaces.end()
347 359 else:
348 360 return 0
349 361
350 362
351 363 def format_screen(strng):
352 364 """Format a string for screen printing.
353 365
354 366 This removes some latex-type format codes."""
355 367 # Paragraph continue
356 368 par_re = re.compile(r'\\$',re.MULTILINE)
357 369 strng = par_re.sub('',strng)
358 370 return strng
359 371
360 372
361 373 def dedent(text):
362 374 """Equivalent of textwrap.dedent that ignores unindented first line.
363 375
364 376 This means it will still dedent strings like:
365 377 '''foo
366 378 is a bar
367 379 '''
368 380
369 381 For use in wrap_paragraphs.
370 382 """
371 383
372 384 if text.startswith('\n'):
373 385 # text starts with blank line, don't ignore the first line
374 386 return textwrap.dedent(text)
375 387
376 388 # split first line
377 389 splits = text.split('\n',1)
378 390 if len(splits) == 1:
379 391 # only one line
380 392 return textwrap.dedent(text)
381 393
382 394 first, rest = splits
383 395 # dedent everything but the first line
384 396 rest = textwrap.dedent(rest)
385 397 return '\n'.join([first, rest])
386 398
387 399
388 400 def wrap_paragraphs(text, ncols=80):
389 401 """Wrap multiple paragraphs to fit a specified width.
390 402
391 403 This is equivalent to textwrap.wrap, but with support for multiple
392 404 paragraphs, as separated by empty lines.
393 405
394 406 Returns
395 407 -------
396 408
397 409 list of complete paragraphs, wrapped to fill `ncols` columns.
398 410 """
399 411 paragraph_re = re.compile(r'\n(\s*\n)+', re.MULTILINE)
400 412 text = dedent(text).strip()
401 413 paragraphs = paragraph_re.split(text)[::2] # every other entry is space
402 414 out_ps = []
403 415 indent_re = re.compile(r'\n\s+', re.MULTILINE)
404 416 for p in paragraphs:
405 417 # presume indentation that survives dedent is meaningful formatting,
406 418 # so don't fill unless text is flush.
407 419 if indent_re.search(p) is None:
408 420 # wrap paragraph
409 421 p = textwrap.fill(p, ncols)
410 422 out_ps.append(p)
411 423 return out_ps
412 424
413 425
414 426 def long_substr(data):
415 427 """Return the longest common substring in a list of strings.
416 428
417 429 Credit: http://stackoverflow.com/questions/2892931/longest-common-substring-from-more-than-two-strings-python
418 430 """
419 431 substr = ''
420 432 if len(data) > 1 and len(data[0]) > 0:
421 433 for i in range(len(data[0])):
422 434 for j in range(len(data[0])-i+1):
423 435 if j > len(substr) and all(data[0][i:i+j] in x for x in data):
424 436 substr = data[0][i:i+j]
425 437 elif len(data) == 1:
426 438 substr = data[0]
427 439 return substr
428 440
429 441
430 442 def strip_email_quotes(text):
431 443 """Strip leading email quotation characters ('>').
432 444
433 445 Removes any combination of leading '>' interspersed with whitespace that
434 446 appears *identically* in all lines of the input text.
435 447
436 448 Parameters
437 449 ----------
438 450 text : str
439 451
440 452 Examples
441 453 --------
442 454
443 455 Simple uses::
444 456
445 457 In [2]: strip_email_quotes('> > text')
446 458 Out[2]: 'text'
447 459
448 460 In [3]: strip_email_quotes('> > text\\n> > more')
449 461 Out[3]: 'text\\nmore'
450 462
451 463 Note how only the common prefix that appears in all lines is stripped::
452 464
453 465 In [4]: strip_email_quotes('> > text\\n> > more\\n> more...')
454 466 Out[4]: '> text\\n> more\\nmore...'
455 467
456 468 So if any line has no quote marks ('>') , then none are stripped from any
457 469 of them ::
458 470
459 471 In [5]: strip_email_quotes('> > text\\n> > more\\nlast different')
460 472 Out[5]: '> > text\\n> > more\\nlast different'
461 473 """
462 474 lines = text.splitlines()
463 475 matches = set()
464 476 for line in lines:
465 477 prefix = re.match(r'^(\s*>[ >]*)', line)
466 478 if prefix:
467 479 matches.add(prefix.group(1))
468 480 else:
469 481 break
470 482 else:
471 483 prefix = long_substr(list(matches))
472 484 if prefix:
473 485 strip = len(prefix)
474 486 text = '\n'.join([ ln[strip:] for ln in lines])
475 487 return text
476 488
477 489
478 490 class EvalFormatter(Formatter):
479 491 """A String Formatter that allows evaluation of simple expressions.
480 492
481 493 Note that this version interprets a : as specifying a format string (as per
482 494 standard string formatting), so if slicing is required, you must explicitly
483 495 create a slice.
484 496
485 497 This is to be used in templating cases, such as the parallel batch
486 498 script templates, where simple arithmetic on arguments is useful.
487 499
488 500 Examples
489 501 --------
490 502
491 503 In [1]: f = EvalFormatter()
492 504 In [2]: f.format('{n//4}', n=8)
493 505 Out [2]: '2'
494 506
495 507 In [3]: f.format("{greeting[slice(2,4)]}", greeting="Hello")
496 508 Out [3]: 'll'
497 509 """
498 510 def get_field(self, name, args, kwargs):
499 511 v = eval(name, kwargs)
500 512 return v, name
501 513
502 514
503 515 @skip_doctest_py3
504 516 class FullEvalFormatter(Formatter):
505 517 """A String Formatter that allows evaluation of simple expressions.
506 518
507 519 Any time a format key is not found in the kwargs,
508 520 it will be tried as an expression in the kwargs namespace.
509 521
510 522 Note that this version allows slicing using [1:2], so you cannot specify
511 523 a format string. Use :class:`EvalFormatter` to permit format strings.
512 524
513 525 Examples
514 526 --------
515 527
516 528 In [1]: f = FullEvalFormatter()
517 529 In [2]: f.format('{n//4}', n=8)
518 530 Out[2]: u'2'
519 531
520 532 In [3]: f.format('{list(range(5))[2:4]}')
521 533 Out[3]: u'[2, 3]'
522 534
523 535 In [4]: f.format('{3*2}')
524 536 Out[4]: u'6'
525 537 """
526 538 # copied from Formatter._vformat with minor changes to allow eval
527 539 # and replace the format_spec code with slicing
528 540 def _vformat(self, format_string, args, kwargs, used_args, recursion_depth):
529 541 if recursion_depth < 0:
530 542 raise ValueError('Max string recursion exceeded')
531 543 result = []
532 544 for literal_text, field_name, format_spec, conversion in \
533 545 self.parse(format_string):
534 546
535 547 # output the literal text
536 548 if literal_text:
537 549 result.append(literal_text)
538 550
539 551 # if there's a field, output it
540 552 if field_name is not None:
541 553 # this is some markup, find the object and do
542 554 # the formatting
543 555
544 556 if format_spec:
545 557 # override format spec, to allow slicing:
546 558 field_name = ':'.join([field_name, format_spec])
547 559
548 560 # eval the contents of the field for the object
549 561 # to be formatted
550 562 obj = eval(field_name, kwargs)
551 563
552 564 # do any conversion on the resulting object
553 565 obj = self.convert_field(obj, conversion)
554 566
555 567 # format the object and append to the result
556 568 result.append(self.format_field(obj, ''))
557 569
558 570 return u''.join(py3compat.cast_unicode(s) for s in result)
559 571
560 572
561 573 @skip_doctest_py3
562 574 class DollarFormatter(FullEvalFormatter):
563 575 """Formatter allowing Itpl style $foo replacement, for names and attribute
564 576 access only. Standard {foo} replacement also works, and allows full
565 577 evaluation of its arguments.
566 578
567 579 Examples
568 580 --------
569 581 In [1]: f = DollarFormatter()
570 582 In [2]: f.format('{n//4}', n=8)
571 583 Out[2]: u'2'
572 584
573 585 In [3]: f.format('23 * 76 is $result', result=23*76)
574 586 Out[3]: u'23 * 76 is 1748'
575 587
576 588 In [4]: f.format('$a or {b}', a=1, b=2)
577 589 Out[4]: u'1 or 2'
578 590 """
579 591 _dollar_pattern = re.compile("(.*?)\$(\$?[\w\.]+)")
580 592 def parse(self, fmt_string):
581 593 for literal_txt, field_name, format_spec, conversion \
582 594 in Formatter.parse(self, fmt_string):
583 595
584 596 # Find $foo patterns in the literal text.
585 597 continue_from = 0
586 598 txt = ""
587 599 for m in self._dollar_pattern.finditer(literal_txt):
588 600 new_txt, new_field = m.group(1,2)
589 601 # $$foo --> $foo
590 602 if new_field.startswith("$"):
591 603 txt += new_txt + new_field
592 604 else:
593 605 yield (txt + new_txt, new_field, "", None)
594 606 txt = ""
595 607 continue_from = m.end()
596 608
597 609 # Re-yield the {foo} style pattern
598 610 yield (txt + literal_txt[continue_from:], field_name, format_spec, conversion)
599 611
600 612 #-----------------------------------------------------------------------------
601 613 # Utils to columnize a list of string
602 614 #-----------------------------------------------------------------------------
603 615
604 616 def _chunks(l, n):
605 617 """Yield successive n-sized chunks from l."""
606 618 for i in xrange(0, len(l), n):
607 619 yield l[i:i+n]
608 620
609 621
610 622 def _find_optimal(rlist , separator_size=2 , displaywidth=80):
611 623 """Calculate optimal info to columnize a list of string"""
612 624 for nrow in range(1, len(rlist)+1) :
613 625 chk = map(max,_chunks(rlist, nrow))
614 626 sumlength = sum(chk)
615 627 ncols = len(chk)
616 628 if sumlength+separator_size*(ncols-1) <= displaywidth :
617 629 break;
618 630 return {'columns_numbers' : ncols,
619 631 'optimal_separator_width':(displaywidth - sumlength)/(ncols-1) if (ncols -1) else 0,
620 632 'rows_numbers' : nrow,
621 633 'columns_width' : chk
622 634 }
623 635
624 636
625 637 def _get_or_default(mylist, i, default=None):
626 638 """return list item number, or default if don't exist"""
627 639 if i >= len(mylist):
628 640 return default
629 641 else :
630 642 return mylist[i]
631 643
632 644
633 645 @skip_doctest
634 646 def compute_item_matrix(items, empty=None, *args, **kwargs) :
635 647 """Returns a nested list, and info to columnize items
636 648
637 649 Parameters
638 650 ----------
639 651
640 652 items :
641 653 list of strings to columize
642 654 empty : (default None)
643 655 default value to fill list if needed
644 656 separator_size : int (default=2)
645 657 How much caracters will be used as a separation between each columns.
646 658 displaywidth : int (default=80)
647 659 The width of the area onto wich the columns should enter
648 660
649 661 Returns
650 662 -------
651 663
652 664 Returns a tuple of (strings_matrix, dict_info)
653 665
654 666 strings_matrix :
655 667
656 668 nested list of string, the outer most list contains as many list as
657 669 rows, the innermost lists have each as many element as colums. If the
658 670 total number of elements in `items` does not equal the product of
659 671 rows*columns, the last element of some lists are filled with `None`.
660 672
661 673 dict_info :
662 674 some info to make columnize easier:
663 675
664 676 columns_numbers : number of columns
665 677 rows_numbers : number of rows
666 678 columns_width : list of with of each columns
667 679 optimal_separator_width : best separator width between columns
668 680
669 681 Examples
670 682 --------
671 683
672 684 In [1]: l = ['aaa','b','cc','d','eeeee','f','g','h','i','j','k','l']
673 685 ...: compute_item_matrix(l,displaywidth=12)
674 686 Out[1]:
675 687 ([['aaa', 'f', 'k'],
676 688 ['b', 'g', 'l'],
677 689 ['cc', 'h', None],
678 690 ['d', 'i', None],
679 691 ['eeeee', 'j', None]],
680 692 {'columns_numbers': 3,
681 693 'columns_width': [5, 1, 1],
682 694 'optimal_separator_width': 2,
683 695 'rows_numbers': 5})
684 696
685 697 """
686 698 info = _find_optimal(map(len, items), *args, **kwargs)
687 699 nrow, ncol = info['rows_numbers'], info['columns_numbers']
688 700 return ([[ _get_or_default(items, c*nrow+i, default=empty) for c in range(ncol) ] for i in range(nrow) ], info)
689 701
690 702
691 703 def columnize(items, separator=' ', displaywidth=80):
692 704 """ Transform a list of strings into a single string with columns.
693 705
694 706 Parameters
695 707 ----------
696 708 items : sequence of strings
697 709 The strings to process.
698 710
699 711 separator : str, optional [default is two spaces]
700 712 The string that separates columns.
701 713
702 714 displaywidth : int, optional [default is 80]
703 715 Width of the display in number of characters.
704 716
705 717 Returns
706 718 -------
707 719 The formatted string.
708 720 """
709 721 if not items :
710 722 return '\n'
711 723 matrix, info = compute_item_matrix(items, separator_size=len(separator), displaywidth=displaywidth)
712 724 fmatrix = [filter(None, x) for x in matrix]
713 725 sjoin = lambda x : separator.join([ y.ljust(w, ' ') for y, w in zip(x, info['columns_width'])])
714 726 return '\n'.join(map(sjoin, fmatrix))+'\n'
715
716
717 def format_date(date):
718 """ Format a datetime object as a string.
719
720 Parameters
721 ----------
722 date : datetime object
723 Date to be formatted.
724
725 Returns
726 -------
727 The formatted date string (ie "July 1, 1990")
728 """
729 if sys.platform == 'win32':
730 return date.strftime("%B {0}, %Y").format(date.day)
731 else:
732 return date.strftime("%B %-d, %Y") No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now