Show More
@@ -1,174 +1,174 | |||||
1 | """Module containing single call export functions.""" |
|
1 | """Module containing single call export functions.""" | |
2 |
|
2 | |||
3 | # Copyright (c) IPython Development Team. |
|
3 | # Copyright (c) IPython Development Team. | |
4 | # Distributed under the terms of the Modified BSD License. |
|
4 | # Distributed under the terms of the Modified BSD License. | |
5 |
|
5 | |||
6 | from functools import wraps |
|
6 | from functools import wraps | |
7 |
|
7 | |||
8 |
from IPython.nbformat. |
|
8 | from IPython.nbformat.v4 import NotebookNode | |
9 | from IPython.utils.decorators import undoc |
|
9 | from IPython.utils.decorators import undoc | |
10 | from IPython.utils.py3compat import string_types |
|
10 | from IPython.utils.py3compat import string_types | |
11 |
|
11 | |||
12 | from .exporter import Exporter |
|
12 | from .exporter import Exporter | |
13 | from .templateexporter import TemplateExporter |
|
13 | from .templateexporter import TemplateExporter | |
14 | from .html import HTMLExporter |
|
14 | from .html import HTMLExporter | |
15 | from .slides import SlidesExporter |
|
15 | from .slides import SlidesExporter | |
16 | from .latex import LatexExporter |
|
16 | from .latex import LatexExporter | |
17 | from .pdf import PDFExporter |
|
17 | from .pdf import PDFExporter | |
18 | from .markdown import MarkdownExporter |
|
18 | from .markdown import MarkdownExporter | |
19 | from .python import PythonExporter |
|
19 | from .python import PythonExporter | |
20 | from .rst import RSTExporter |
|
20 | from .rst import RSTExporter | |
21 | from .notebook import NotebookExporter |
|
21 | from .notebook import NotebookExporter | |
22 |
|
22 | |||
23 | #----------------------------------------------------------------------------- |
|
23 | #----------------------------------------------------------------------------- | |
24 | # Classes |
|
24 | # Classes | |
25 | #----------------------------------------------------------------------------- |
|
25 | #----------------------------------------------------------------------------- | |
26 |
|
26 | |||
27 | @undoc |
|
27 | @undoc | |
28 | def DocDecorator(f): |
|
28 | def DocDecorator(f): | |
29 |
|
29 | |||
30 | #Set docstring of function |
|
30 | #Set docstring of function | |
31 | f.__doc__ = f.__doc__ + """ |
|
31 | f.__doc__ = f.__doc__ + """ | |
32 | nb : :class:`~{nbnode_mod}.NotebookNode` |
|
32 | nb : :class:`~{nbnode_mod}.NotebookNode` | |
33 | The notebook to export. |
|
33 | The notebook to export. | |
34 | config : config (optional, keyword arg) |
|
34 | config : config (optional, keyword arg) | |
35 | User configuration instance. |
|
35 | User configuration instance. | |
36 | resources : dict (optional, keyword arg) |
|
36 | resources : dict (optional, keyword arg) | |
37 | Resources used in the conversion process. |
|
37 | Resources used in the conversion process. | |
38 |
|
38 | |||
39 | Returns |
|
39 | Returns | |
40 | ------- |
|
40 | ------- | |
41 | tuple- output, resources, exporter_instance |
|
41 | tuple- output, resources, exporter_instance | |
42 | output : str |
|
42 | output : str | |
43 | Jinja 2 output. This is the resulting converted notebook. |
|
43 | Jinja 2 output. This is the resulting converted notebook. | |
44 | resources : dictionary |
|
44 | resources : dictionary | |
45 | Dictionary of resources used prior to and during the conversion |
|
45 | Dictionary of resources used prior to and during the conversion | |
46 | process. |
|
46 | process. | |
47 | exporter_instance : Exporter |
|
47 | exporter_instance : Exporter | |
48 | Instance of the Exporter class used to export the document. Useful |
|
48 | Instance of the Exporter class used to export the document. Useful | |
49 | to caller because it provides a 'file_extension' property which |
|
49 | to caller because it provides a 'file_extension' property which | |
50 | specifies what extension the output should be saved as. |
|
50 | specifies what extension the output should be saved as. | |
51 |
|
51 | |||
52 | Notes |
|
52 | Notes | |
53 | ----- |
|
53 | ----- | |
54 | WARNING: API WILL CHANGE IN FUTURE RELEASES OF NBCONVERT |
|
54 | WARNING: API WILL CHANGE IN FUTURE RELEASES OF NBCONVERT | |
55 | """.format(nbnode_mod=NotebookNode.__module__) |
|
55 | """.format(nbnode_mod=NotebookNode.__module__) | |
56 |
|
56 | |||
57 | @wraps(f) |
|
57 | @wraps(f) | |
58 | def decorator(*args, **kwargs): |
|
58 | def decorator(*args, **kwargs): | |
59 | return f(*args, **kwargs) |
|
59 | return f(*args, **kwargs) | |
60 |
|
60 | |||
61 | return decorator |
|
61 | return decorator | |
62 |
|
62 | |||
63 |
|
63 | |||
64 | #----------------------------------------------------------------------------- |
|
64 | #----------------------------------------------------------------------------- | |
65 | # Functions |
|
65 | # Functions | |
66 | #----------------------------------------------------------------------------- |
|
66 | #----------------------------------------------------------------------------- | |
67 |
|
67 | |||
68 | __all__ = [ |
|
68 | __all__ = [ | |
69 | 'export', |
|
69 | 'export', | |
70 | 'export_html', |
|
70 | 'export_html', | |
71 | 'export_custom', |
|
71 | 'export_custom', | |
72 | 'export_slides', |
|
72 | 'export_slides', | |
73 | 'export_latex', |
|
73 | 'export_latex', | |
74 | 'export_pdf', |
|
74 | 'export_pdf', | |
75 | 'export_markdown', |
|
75 | 'export_markdown', | |
76 | 'export_python', |
|
76 | 'export_python', | |
77 | 'export_rst', |
|
77 | 'export_rst', | |
78 | 'export_by_name', |
|
78 | 'export_by_name', | |
79 | 'get_export_names', |
|
79 | 'get_export_names', | |
80 | 'ExporterNameError' |
|
80 | 'ExporterNameError' | |
81 | ] |
|
81 | ] | |
82 |
|
82 | |||
83 |
|
83 | |||
84 | class ExporterNameError(NameError): |
|
84 | class ExporterNameError(NameError): | |
85 | pass |
|
85 | pass | |
86 |
|
86 | |||
87 | @DocDecorator |
|
87 | @DocDecorator | |
88 | def export(exporter, nb, **kw): |
|
88 | def export(exporter, nb, **kw): | |
89 | """ |
|
89 | """ | |
90 | Export a notebook object using specific exporter class. |
|
90 | Export a notebook object using specific exporter class. | |
91 |
|
91 | |||
92 | Parameters |
|
92 | Parameters | |
93 | ---------- |
|
93 | ---------- | |
94 | exporter : class:`~IPython.nbconvert.exporters.exporter.Exporter` class or instance |
|
94 | exporter : class:`~IPython.nbconvert.exporters.exporter.Exporter` class or instance | |
95 | Class type or instance of the exporter that should be used. If the |
|
95 | Class type or instance of the exporter that should be used. If the | |
96 | method initializes it's own instance of the class, it is ASSUMED that |
|
96 | method initializes it's own instance of the class, it is ASSUMED that | |
97 | the class type provided exposes a constructor (``__init__``) with the same |
|
97 | the class type provided exposes a constructor (``__init__``) with the same | |
98 | signature as the base Exporter class. |
|
98 | signature as the base Exporter class. | |
99 | """ |
|
99 | """ | |
100 |
|
100 | |||
101 | #Check arguments |
|
101 | #Check arguments | |
102 | if exporter is None: |
|
102 | if exporter is None: | |
103 | raise TypeError("Exporter is None") |
|
103 | raise TypeError("Exporter is None") | |
104 | elif not isinstance(exporter, Exporter) and not issubclass(exporter, Exporter): |
|
104 | elif not isinstance(exporter, Exporter) and not issubclass(exporter, Exporter): | |
105 | raise TypeError("exporter does not inherit from Exporter (base)") |
|
105 | raise TypeError("exporter does not inherit from Exporter (base)") | |
106 | if nb is None: |
|
106 | if nb is None: | |
107 | raise TypeError("nb is None") |
|
107 | raise TypeError("nb is None") | |
108 |
|
108 | |||
109 | #Create the exporter |
|
109 | #Create the exporter | |
110 | resources = kw.pop('resources', None) |
|
110 | resources = kw.pop('resources', None) | |
111 | if isinstance(exporter, Exporter): |
|
111 | if isinstance(exporter, Exporter): | |
112 | exporter_instance = exporter |
|
112 | exporter_instance = exporter | |
113 | else: |
|
113 | else: | |
114 | exporter_instance = exporter(**kw) |
|
114 | exporter_instance = exporter(**kw) | |
115 |
|
115 | |||
116 | #Try to convert the notebook using the appropriate conversion function. |
|
116 | #Try to convert the notebook using the appropriate conversion function. | |
117 | if isinstance(nb, NotebookNode): |
|
117 | if isinstance(nb, NotebookNode): | |
118 | output, resources = exporter_instance.from_notebook_node(nb, resources) |
|
118 | output, resources = exporter_instance.from_notebook_node(nb, resources) | |
119 | elif isinstance(nb, string_types): |
|
119 | elif isinstance(nb, string_types): | |
120 | output, resources = exporter_instance.from_filename(nb, resources) |
|
120 | output, resources = exporter_instance.from_filename(nb, resources) | |
121 | else: |
|
121 | else: | |
122 | output, resources = exporter_instance.from_file(nb, resources) |
|
122 | output, resources = exporter_instance.from_file(nb, resources) | |
123 | return output, resources |
|
123 | return output, resources | |
124 |
|
124 | |||
125 | exporter_map = dict( |
|
125 | exporter_map = dict( | |
126 | custom=TemplateExporter, |
|
126 | custom=TemplateExporter, | |
127 | html=HTMLExporter, |
|
127 | html=HTMLExporter, | |
128 | slides=SlidesExporter, |
|
128 | slides=SlidesExporter, | |
129 | latex=LatexExporter, |
|
129 | latex=LatexExporter, | |
130 | pdf=PDFExporter, |
|
130 | pdf=PDFExporter, | |
131 | markdown=MarkdownExporter, |
|
131 | markdown=MarkdownExporter, | |
132 | python=PythonExporter, |
|
132 | python=PythonExporter, | |
133 | rst=RSTExporter, |
|
133 | rst=RSTExporter, | |
134 | notebook=NotebookExporter, |
|
134 | notebook=NotebookExporter, | |
135 | ) |
|
135 | ) | |
136 |
|
136 | |||
137 | def _make_exporter(name, E): |
|
137 | def _make_exporter(name, E): | |
138 | """make an export_foo function from a short key and Exporter class E""" |
|
138 | """make an export_foo function from a short key and Exporter class E""" | |
139 | def _export(nb, **kw): |
|
139 | def _export(nb, **kw): | |
140 | return export(E, nb, **kw) |
|
140 | return export(E, nb, **kw) | |
141 | _export.__doc__ = """Export a notebook object to {0} format""".format(name) |
|
141 | _export.__doc__ = """Export a notebook object to {0} format""".format(name) | |
142 | return _export |
|
142 | return _export | |
143 |
|
143 | |||
144 | g = globals() |
|
144 | g = globals() | |
145 |
|
145 | |||
146 | for name, E in exporter_map.items(): |
|
146 | for name, E in exporter_map.items(): | |
147 | g['export_%s' % name] = DocDecorator(_make_exporter(name, E)) |
|
147 | g['export_%s' % name] = DocDecorator(_make_exporter(name, E)) | |
148 |
|
148 | |||
149 | @DocDecorator |
|
149 | @DocDecorator | |
150 | def export_by_name(format_name, nb, **kw): |
|
150 | def export_by_name(format_name, nb, **kw): | |
151 | """ |
|
151 | """ | |
152 | Export a notebook object to a template type by its name. Reflection |
|
152 | Export a notebook object to a template type by its name. Reflection | |
153 | (Inspect) is used to find the template's corresponding explicit export |
|
153 | (Inspect) is used to find the template's corresponding explicit export | |
154 | method defined in this module. That method is then called directly. |
|
154 | method defined in this module. That method is then called directly. | |
155 |
|
155 | |||
156 | Parameters |
|
156 | Parameters | |
157 | ---------- |
|
157 | ---------- | |
158 | format_name : str |
|
158 | format_name : str | |
159 | Name of the template style to export to. |
|
159 | Name of the template style to export to. | |
160 | """ |
|
160 | """ | |
161 |
|
161 | |||
162 | function_name = "export_" + format_name.lower() |
|
162 | function_name = "export_" + format_name.lower() | |
163 |
|
163 | |||
164 | if function_name in globals(): |
|
164 | if function_name in globals(): | |
165 | return globals()[function_name](nb, **kw) |
|
165 | return globals()[function_name](nb, **kw) | |
166 | else: |
|
166 | else: | |
167 | raise ExporterNameError("template for `%s` not found" % function_name) |
|
167 | raise ExporterNameError("template for `%s` not found" % function_name) | |
168 |
|
168 | |||
169 |
|
169 | |||
170 | def get_export_names(): |
|
170 | def get_export_names(): | |
171 | """Return a list of the currently supported export targets |
|
171 | """Return a list of the currently supported export targets | |
172 |
|
172 | |||
173 | WARNING: API WILL CHANGE IN FUTURE RELEASES OF NBCONVERT""" |
|
173 | WARNING: API WILL CHANGE IN FUTURE RELEASES OF NBCONVERT""" | |
174 | return sorted(exporter_map.keys()) |
|
174 | return sorted(exporter_map.keys()) |
@@ -1,276 +1,259 | |||||
1 | """This module defines a base Exporter class. For Jinja template-based export, |
|
1 | """This module defines a base Exporter class. For Jinja template-based export, | |
2 | see templateexporter.py. |
|
2 | see templateexporter.py. | |
3 | """ |
|
3 | """ | |
4 |
|
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 |
|
5 | |||
17 | from __future__ import print_function, absolute_import |
|
6 | from __future__ import print_function, absolute_import | |
18 |
|
7 | |||
19 | # Stdlib imports |
|
|||
20 | import io |
|
8 | import io | |
21 | import os |
|
9 | import os | |
22 | import copy |
|
10 | import copy | |
23 | import collections |
|
11 | import collections | |
24 | import datetime |
|
12 | import datetime | |
25 |
|
13 | |||
26 |
|
||||
27 | # IPython imports |
|
|||
28 | from IPython.config.configurable import LoggingConfigurable |
|
14 | from IPython.config.configurable import LoggingConfigurable | |
29 | from IPython.config import Config |
|
15 | from IPython.config import Config | |
30 |
from IPython |
|
16 | from IPython import nbformat | |
31 | from IPython.utils.traitlets import MetaHasTraits, Unicode, List |
|
17 | from IPython.utils.traitlets import MetaHasTraits, Unicode, List | |
32 | from IPython.utils.importstring import import_item |
|
18 | from IPython.utils.importstring import import_item | |
33 | from IPython.utils import text, py3compat |
|
19 | from IPython.utils import text, py3compat | |
34 |
|
20 | |||
35 | #----------------------------------------------------------------------------- |
|
|||
36 | # Class |
|
|||
37 | #----------------------------------------------------------------------------- |
|
|||
38 |
|
21 | |||
39 | class ResourcesDict(collections.defaultdict): |
|
22 | class ResourcesDict(collections.defaultdict): | |
40 | def __missing__(self, key): |
|
23 | def __missing__(self, key): | |
41 | return '' |
|
24 | return '' | |
42 |
|
25 | |||
43 |
|
26 | |||
44 | class Exporter(LoggingConfigurable): |
|
27 | class Exporter(LoggingConfigurable): | |
45 | """ |
|
28 | """ | |
46 | Class containing methods that sequentially run a list of preprocessors on a |
|
29 | Class containing methods that sequentially run a list of preprocessors on a | |
47 | NotebookNode object and then return the modified NotebookNode object and |
|
30 | NotebookNode object and then return the modified NotebookNode object and | |
48 | accompanying resources dict. |
|
31 | accompanying resources dict. | |
49 | """ |
|
32 | """ | |
50 |
|
33 | |||
51 | file_extension = Unicode( |
|
34 | file_extension = Unicode( | |
52 | 'txt', config=True, |
|
35 | 'txt', config=True, | |
53 | help="Extension of the file that should be written to disk" |
|
36 | help="Extension of the file that should be written to disk" | |
54 | ) |
|
37 | ) | |
55 |
|
38 | |||
56 | # MIME type of the result file, for HTTP response headers. |
|
39 | # MIME type of the result file, for HTTP response headers. | |
57 | # This is *not* a traitlet, because we want to be able to access it from |
|
40 | # This is *not* a traitlet, because we want to be able to access it from | |
58 | # the class, not just on instances. |
|
41 | # the class, not just on instances. | |
59 | output_mimetype = '' |
|
42 | output_mimetype = '' | |
60 |
|
43 | |||
61 | #Configurability, allows the user to easily add filters and preprocessors. |
|
44 | #Configurability, allows the user to easily add filters and preprocessors. | |
62 | preprocessors = List(config=True, |
|
45 | preprocessors = List(config=True, | |
63 | help="""List of preprocessors, by name or namespace, to enable.""") |
|
46 | help="""List of preprocessors, by name or namespace, to enable.""") | |
64 |
|
47 | |||
65 | _preprocessors = List() |
|
48 | _preprocessors = List() | |
66 |
|
49 | |||
67 | default_preprocessors = List(['IPython.nbconvert.preprocessors.coalesce_streams', |
|
50 | default_preprocessors = List(['IPython.nbconvert.preprocessors.coalesce_streams', | |
68 | 'IPython.nbconvert.preprocessors.SVG2PDFPreprocessor', |
|
51 | 'IPython.nbconvert.preprocessors.SVG2PDFPreprocessor', | |
69 | 'IPython.nbconvert.preprocessors.ExtractOutputPreprocessor', |
|
52 | 'IPython.nbconvert.preprocessors.ExtractOutputPreprocessor', | |
70 | 'IPython.nbconvert.preprocessors.CSSHTMLHeaderPreprocessor', |
|
53 | 'IPython.nbconvert.preprocessors.CSSHTMLHeaderPreprocessor', | |
71 | 'IPython.nbconvert.preprocessors.RevealHelpPreprocessor', |
|
54 | 'IPython.nbconvert.preprocessors.RevealHelpPreprocessor', | |
72 | 'IPython.nbconvert.preprocessors.LatexPreprocessor', |
|
55 | 'IPython.nbconvert.preprocessors.LatexPreprocessor', | |
73 | 'IPython.nbconvert.preprocessors.ClearOutputPreprocessor', |
|
56 | 'IPython.nbconvert.preprocessors.ClearOutputPreprocessor', | |
74 | 'IPython.nbconvert.preprocessors.ExecutePreprocessor', |
|
57 | 'IPython.nbconvert.preprocessors.ExecutePreprocessor', | |
75 | 'IPython.nbconvert.preprocessors.HighlightMagicsPreprocessor'], |
|
58 | 'IPython.nbconvert.preprocessors.HighlightMagicsPreprocessor'], | |
76 | config=True, |
|
59 | config=True, | |
77 | help="""List of preprocessors available by default, by name, namespace, |
|
60 | help="""List of preprocessors available by default, by name, namespace, | |
78 | instance, or type.""") |
|
61 | instance, or type.""") | |
79 |
|
62 | |||
80 |
|
63 | |||
81 | def __init__(self, config=None, **kw): |
|
64 | def __init__(self, config=None, **kw): | |
82 | """ |
|
65 | """ | |
83 | Public constructor |
|
66 | Public constructor | |
84 |
|
67 | |||
85 | Parameters |
|
68 | Parameters | |
86 | ---------- |
|
69 | ---------- | |
87 | config : config |
|
70 | config : config | |
88 | User configuration instance. |
|
71 | User configuration instance. | |
89 | """ |
|
72 | """ | |
90 | with_default_config = self.default_config |
|
73 | with_default_config = self.default_config | |
91 | if config: |
|
74 | if config: | |
92 | with_default_config.merge(config) |
|
75 | with_default_config.merge(config) | |
93 |
|
76 | |||
94 | super(Exporter, self).__init__(config=with_default_config, **kw) |
|
77 | super(Exporter, self).__init__(config=with_default_config, **kw) | |
95 |
|
78 | |||
96 | self._init_preprocessors() |
|
79 | self._init_preprocessors() | |
97 |
|
80 | |||
98 |
|
81 | |||
99 | @property |
|
82 | @property | |
100 | def default_config(self): |
|
83 | def default_config(self): | |
101 | return Config() |
|
84 | return Config() | |
102 |
|
85 | |||
103 | def from_notebook_node(self, nb, resources=None, **kw): |
|
86 | def from_notebook_node(self, nb, resources=None, **kw): | |
104 | """ |
|
87 | """ | |
105 | Convert a notebook from a notebook node instance. |
|
88 | Convert a notebook from a notebook node instance. | |
106 |
|
89 | |||
107 | Parameters |
|
90 | Parameters | |
108 | ---------- |
|
91 | ---------- | |
109 | nb : :class:`~IPython.nbformat.current.NotebookNode` |
|
92 | nb : :class:`~IPython.nbformat.current.NotebookNode` | |
110 | Notebook node |
|
93 | Notebook node (dict-like with attr-access) | |
111 | resources : dict |
|
94 | resources : dict | |
112 | Additional resources that can be accessed read/write by |
|
95 | Additional resources that can be accessed read/write by | |
113 | preprocessors and filters. |
|
96 | preprocessors and filters. | |
114 | **kw |
|
97 | **kw | |
115 | Ignored (?) |
|
98 | Ignored (?) | |
116 | """ |
|
99 | """ | |
117 | nb_copy = copy.deepcopy(nb) |
|
100 | nb_copy = copy.deepcopy(nb) | |
118 | resources = self._init_resources(resources) |
|
101 | resources = self._init_resources(resources) | |
119 |
|
102 | |||
120 | if 'language' in nb['metadata']: |
|
103 | if 'language' in nb['metadata']: | |
121 | resources['language'] = nb['metadata']['language'].lower() |
|
104 | resources['language'] = nb['metadata']['language'].lower() | |
122 |
|
105 | |||
123 | # Preprocess |
|
106 | # Preprocess | |
124 | nb_copy, resources = self._preprocess(nb_copy, resources) |
|
107 | nb_copy, resources = self._preprocess(nb_copy, resources) | |
125 |
|
108 | |||
126 | return nb_copy, resources |
|
109 | return nb_copy, resources | |
127 |
|
110 | |||
128 |
|
111 | |||
129 | def from_filename(self, filename, resources=None, **kw): |
|
112 | def from_filename(self, filename, resources=None, **kw): | |
130 | """ |
|
113 | """ | |
131 | Convert a notebook from a notebook file. |
|
114 | Convert a notebook from a notebook file. | |
132 |
|
115 | |||
133 | Parameters |
|
116 | Parameters | |
134 | ---------- |
|
117 | ---------- | |
135 | filename : str |
|
118 | filename : str | |
136 | Full filename of the notebook file to open and convert. |
|
119 | Full filename of the notebook file to open and convert. | |
137 | """ |
|
120 | """ | |
138 |
|
121 | |||
139 | # Pull the metadata from the filesystem. |
|
122 | # Pull the metadata from the filesystem. | |
140 | if resources is None: |
|
123 | if resources is None: | |
141 | resources = ResourcesDict() |
|
124 | resources = ResourcesDict() | |
142 | if not 'metadata' in resources or resources['metadata'] == '': |
|
125 | if not 'metadata' in resources or resources['metadata'] == '': | |
143 | resources['metadata'] = ResourcesDict() |
|
126 | resources['metadata'] = ResourcesDict() | |
144 | basename = os.path.basename(filename) |
|
127 | basename = os.path.basename(filename) | |
145 | notebook_name = basename[:basename.rfind('.')] |
|
128 | notebook_name = basename[:basename.rfind('.')] | |
146 | resources['metadata']['name'] = notebook_name |
|
129 | resources['metadata']['name'] = notebook_name | |
147 |
|
130 | |||
148 | modified_date = datetime.datetime.fromtimestamp(os.path.getmtime(filename)) |
|
131 | modified_date = datetime.datetime.fromtimestamp(os.path.getmtime(filename)) | |
149 | resources['metadata']['modified_date'] = modified_date.strftime(text.date_format) |
|
132 | resources['metadata']['modified_date'] = modified_date.strftime(text.date_format) | |
150 |
|
133 | |||
151 | with io.open(filename, encoding='utf-8') as f: |
|
134 | with io.open(filename, encoding='utf-8') as f: | |
152 |
return self.from_notebook_node(nbformat.read(f, |
|
135 | return self.from_notebook_node(nbformat.read(f, as_version=4), resources=resources, **kw) | |
153 |
|
136 | |||
154 |
|
137 | |||
155 | def from_file(self, file_stream, resources=None, **kw): |
|
138 | def from_file(self, file_stream, resources=None, **kw): | |
156 | """ |
|
139 | """ | |
157 | Convert a notebook from a notebook file. |
|
140 | Convert a notebook from a notebook file. | |
158 |
|
141 | |||
159 | Parameters |
|
142 | Parameters | |
160 | ---------- |
|
143 | ---------- | |
161 | file_stream : file-like object |
|
144 | file_stream : file-like object | |
162 | Notebook file-like object to convert. |
|
145 | Notebook file-like object to convert. | |
163 | """ |
|
146 | """ | |
164 |
return self.from_notebook_node(nbformat.read(file_stream, |
|
147 | return self.from_notebook_node(nbformat.read(file_stream, as_version=4), resources=resources, **kw) | |
165 |
|
148 | |||
166 |
|
149 | |||
167 | def register_preprocessor(self, preprocessor, enabled=False): |
|
150 | def register_preprocessor(self, preprocessor, enabled=False): | |
168 | """ |
|
151 | """ | |
169 | Register a preprocessor. |
|
152 | Register a preprocessor. | |
170 | Preprocessors are classes that act upon the notebook before it is |
|
153 | Preprocessors are classes that act upon the notebook before it is | |
171 | passed into the Jinja templating engine. preprocessors are also |
|
154 | passed into the Jinja templating engine. preprocessors are also | |
172 | capable of passing additional information to the Jinja |
|
155 | capable of passing additional information to the Jinja | |
173 | templating engine. |
|
156 | templating engine. | |
174 |
|
157 | |||
175 | Parameters |
|
158 | Parameters | |
176 | ---------- |
|
159 | ---------- | |
177 | preprocessor : preprocessor |
|
160 | preprocessor : preprocessor | |
178 | """ |
|
161 | """ | |
179 | if preprocessor is None: |
|
162 | if preprocessor is None: | |
180 | raise TypeError('preprocessor') |
|
163 | raise TypeError('preprocessor') | |
181 | isclass = isinstance(preprocessor, type) |
|
164 | isclass = isinstance(preprocessor, type) | |
182 | constructed = not isclass |
|
165 | constructed = not isclass | |
183 |
|
166 | |||
184 | # Handle preprocessor's registration based on it's type |
|
167 | # Handle preprocessor's registration based on it's type | |
185 | if constructed and isinstance(preprocessor, py3compat.string_types): |
|
168 | if constructed and isinstance(preprocessor, py3compat.string_types): | |
186 | # Preprocessor is a string, import the namespace and recursively call |
|
169 | # Preprocessor is a string, import the namespace and recursively call | |
187 | # this register_preprocessor method |
|
170 | # this register_preprocessor method | |
188 | preprocessor_cls = import_item(preprocessor) |
|
171 | preprocessor_cls = import_item(preprocessor) | |
189 | return self.register_preprocessor(preprocessor_cls, enabled) |
|
172 | return self.register_preprocessor(preprocessor_cls, enabled) | |
190 |
|
173 | |||
191 | if constructed and hasattr(preprocessor, '__call__'): |
|
174 | if constructed and hasattr(preprocessor, '__call__'): | |
192 | # Preprocessor is a function, no need to construct it. |
|
175 | # Preprocessor is a function, no need to construct it. | |
193 | # Register and return the preprocessor. |
|
176 | # Register and return the preprocessor. | |
194 | if enabled: |
|
177 | if enabled: | |
195 | preprocessor.enabled = True |
|
178 | preprocessor.enabled = True | |
196 | self._preprocessors.append(preprocessor) |
|
179 | self._preprocessors.append(preprocessor) | |
197 | return preprocessor |
|
180 | return preprocessor | |
198 |
|
181 | |||
199 | elif isclass and isinstance(preprocessor, MetaHasTraits): |
|
182 | elif isclass and isinstance(preprocessor, MetaHasTraits): | |
200 | # Preprocessor is configurable. Make sure to pass in new default for |
|
183 | # Preprocessor is configurable. Make sure to pass in new default for | |
201 | # the enabled flag if one was specified. |
|
184 | # the enabled flag if one was specified. | |
202 | self.register_preprocessor(preprocessor(parent=self), enabled) |
|
185 | self.register_preprocessor(preprocessor(parent=self), enabled) | |
203 |
|
186 | |||
204 | elif isclass: |
|
187 | elif isclass: | |
205 | # Preprocessor is not configurable, construct it |
|
188 | # Preprocessor is not configurable, construct it | |
206 | self.register_preprocessor(preprocessor(), enabled) |
|
189 | self.register_preprocessor(preprocessor(), enabled) | |
207 |
|
190 | |||
208 | else: |
|
191 | else: | |
209 | # Preprocessor is an instance of something without a __call__ |
|
192 | # Preprocessor is an instance of something without a __call__ | |
210 | # attribute. |
|
193 | # attribute. | |
211 | raise TypeError('preprocessor') |
|
194 | raise TypeError('preprocessor') | |
212 |
|
195 | |||
213 |
|
196 | |||
214 | def _init_preprocessors(self): |
|
197 | def _init_preprocessors(self): | |
215 | """ |
|
198 | """ | |
216 | Register all of the preprocessors needed for this exporter, disabled |
|
199 | Register all of the preprocessors needed for this exporter, disabled | |
217 | unless specified explicitly. |
|
200 | unless specified explicitly. | |
218 | """ |
|
201 | """ | |
219 | self._preprocessors = [] |
|
202 | self._preprocessors = [] | |
220 |
|
203 | |||
221 | # Load default preprocessors (not necessarly enabled by default). |
|
204 | # Load default preprocessors (not necessarly enabled by default). | |
222 | for preprocessor in self.default_preprocessors: |
|
205 | for preprocessor in self.default_preprocessors: | |
223 | self.register_preprocessor(preprocessor) |
|
206 | self.register_preprocessor(preprocessor) | |
224 |
|
207 | |||
225 | # Load user-specified preprocessors. Enable by default. |
|
208 | # Load user-specified preprocessors. Enable by default. | |
226 | for preprocessor in self.preprocessors: |
|
209 | for preprocessor in self.preprocessors: | |
227 | self.register_preprocessor(preprocessor, enabled=True) |
|
210 | self.register_preprocessor(preprocessor, enabled=True) | |
228 |
|
211 | |||
229 |
|
212 | |||
230 | def _init_resources(self, resources): |
|
213 | def _init_resources(self, resources): | |
231 |
|
214 | |||
232 | #Make sure the resources dict is of ResourcesDict type. |
|
215 | #Make sure the resources dict is of ResourcesDict type. | |
233 | if resources is None: |
|
216 | if resources is None: | |
234 | resources = ResourcesDict() |
|
217 | resources = ResourcesDict() | |
235 | if not isinstance(resources, ResourcesDict): |
|
218 | if not isinstance(resources, ResourcesDict): | |
236 | new_resources = ResourcesDict() |
|
219 | new_resources = ResourcesDict() | |
237 | new_resources.update(resources) |
|
220 | new_resources.update(resources) | |
238 | resources = new_resources |
|
221 | resources = new_resources | |
239 |
|
222 | |||
240 | #Make sure the metadata extension exists in resources |
|
223 | #Make sure the metadata extension exists in resources | |
241 | if 'metadata' in resources: |
|
224 | if 'metadata' in resources: | |
242 | if not isinstance(resources['metadata'], ResourcesDict): |
|
225 | if not isinstance(resources['metadata'], ResourcesDict): | |
243 | resources['metadata'] = ResourcesDict(resources['metadata']) |
|
226 | resources['metadata'] = ResourcesDict(resources['metadata']) | |
244 | else: |
|
227 | else: | |
245 | resources['metadata'] = ResourcesDict() |
|
228 | resources['metadata'] = ResourcesDict() | |
246 | if not resources['metadata']['name']: |
|
229 | if not resources['metadata']['name']: | |
247 | resources['metadata']['name'] = 'Notebook' |
|
230 | resources['metadata']['name'] = 'Notebook' | |
248 |
|
231 | |||
249 | #Set the output extension |
|
232 | #Set the output extension | |
250 | resources['output_extension'] = self.file_extension |
|
233 | resources['output_extension'] = self.file_extension | |
251 | return resources |
|
234 | return resources | |
252 |
|
235 | |||
253 |
|
236 | |||
254 | def _preprocess(self, nb, resources): |
|
237 | def _preprocess(self, nb, resources): | |
255 | """ |
|
238 | """ | |
256 | Preprocess the notebook before passing it into the Jinja engine. |
|
239 | Preprocess the notebook before passing it into the Jinja engine. | |
257 | To preprocess the notebook is to apply all of the |
|
240 | To preprocess the notebook is to apply all of the | |
258 |
|
241 | |||
259 | Parameters |
|
242 | Parameters | |
260 | ---------- |
|
243 | ---------- | |
261 | nb : notebook node |
|
244 | nb : notebook node | |
262 | notebook that is being exported. |
|
245 | notebook that is being exported. | |
263 | resources : a dict of additional resources that |
|
246 | resources : a dict of additional resources that | |
264 | can be accessed read/write by preprocessors |
|
247 | can be accessed read/write by preprocessors | |
265 | """ |
|
248 | """ | |
266 |
|
249 | |||
267 | # Do a copy.deepcopy first, |
|
250 | # Do a copy.deepcopy first, | |
268 | # we are never safe enough with what the preprocessors could do. |
|
251 | # we are never safe enough with what the preprocessors could do. | |
269 | nbc = copy.deepcopy(nb) |
|
252 | nbc = copy.deepcopy(nb) | |
270 | resc = copy.deepcopy(resources) |
|
253 | resc = copy.deepcopy(resources) | |
271 |
|
254 | |||
272 | #Run each preprocessor on the notebook. Carry the output along |
|
255 | #Run each preprocessor on the notebook. Carry the output along | |
273 | #to each preprocessor |
|
256 | #to each preprocessor | |
274 | for preprocessor in self._preprocessors: |
|
257 | for preprocessor in self._preprocessors: | |
275 | nbc, resc = preprocessor(nbc, resc) |
|
258 | nbc, resc = preprocessor(nbc, resc) | |
276 | return nbc, resc |
|
259 | return nbc, resc |
@@ -1,32 +1,32 | |||||
1 | """NotebookExporter class""" |
|
1 | """NotebookExporter class""" | |
2 |
|
2 | |||
3 | # Copyright (c) IPython Development Team. |
|
3 | # Copyright (c) IPython Development Team. | |
4 | # Distributed under the terms of the Modified BSD License. |
|
4 | # Distributed under the terms of the Modified BSD License. | |
5 |
|
5 | |||
6 | from .exporter import Exporter |
|
6 | from .exporter import Exporter | |
7 |
from IPython |
|
7 | from IPython import nbformat | |
8 | from IPython.utils.traitlets import Enum |
|
8 | from IPython.utils.traitlets import Enum | |
9 |
|
9 | |||
10 | class NotebookExporter(Exporter): |
|
10 | class NotebookExporter(Exporter): | |
11 | """Exports to an IPython notebook.""" |
|
11 | """Exports to an IPython notebook.""" | |
12 |
|
12 | |||
13 |
nbformat_version = Enum(list( |
|
13 | nbformat_version = Enum(list(nbformat.versions), | |
14 | default_value=nbformat.current_nbformat, |
|
14 | default_value=nbformat.current_nbformat, | |
15 | config=True, |
|
15 | config=True, | |
16 | help="""The nbformat version to write. |
|
16 | help="""The nbformat version to write. | |
17 | Use this to downgrade notebooks. |
|
17 | Use this to downgrade notebooks. | |
18 | """ |
|
18 | """ | |
19 | ) |
|
19 | ) | |
20 | def _file_extension_default(self): |
|
20 | def _file_extension_default(self): | |
21 | return 'ipynb' |
|
21 | return 'ipynb' | |
22 |
|
22 | |||
23 | output_mimetype = 'application/json' |
|
23 | output_mimetype = 'application/json' | |
24 |
|
24 | |||
25 | def from_notebook_node(self, nb, resources=None, **kw): |
|
25 | def from_notebook_node(self, nb, resources=None, **kw): | |
26 | nb_copy, resources = super(NotebookExporter, self).from_notebook_node(nb, resources, **kw) |
|
26 | nb_copy, resources = super(NotebookExporter, self).from_notebook_node(nb, resources, **kw) | |
27 |
if self.nbformat_version != nbformat |
|
27 | if self.nbformat_version != nb_copy.nbformat: | |
28 | resources['output_suffix'] = '.v%i' % self.nbformat_version |
|
28 | resources['output_suffix'] = '.v%i' % self.nbformat_version | |
29 | else: |
|
29 | else: | |
30 | resources['output_suffix'] = '.nbconvert' |
|
30 | resources['output_suffix'] = '.nbconvert' | |
31 | output = nbformat.writes(nb_copy, version=self.nbformat_version) |
|
31 | output = nbformat.writes(nb_copy, version=self.nbformat_version) | |
32 | return output, resources |
|
32 | return output, resources |
@@ -1,102 +1,90 | |||||
1 | """ |
|
1 | """ | |
2 | Module with tests for export.py |
|
2 | Module with tests for export.py | |
3 | """ |
|
3 | """ | |
4 |
|
4 | |||
5 | #----------------------------------------------------------------------------- |
|
5 | # Copyright (c) IPython Development Team. | |
6 | # Copyright (c) 2013, the IPython Development Team. |
|
|||
7 | # |
|
|||
8 | # Distributed under the terms of the Modified BSD License. |
|
6 | # 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 |
|
7 | |||
17 | import os |
|
8 | import os | |
18 |
|
9 | |||
19 |
from IPython |
|
10 | from IPython import nbformat | |
20 |
|
11 | |||
21 | from .base import ExportersTestsBase |
|
12 | from .base import ExportersTestsBase | |
22 | from ..export import * |
|
13 | from ..export import * | |
23 | from ..python import PythonExporter |
|
14 | from ..python import PythonExporter | |
24 |
|
15 | |||
25 | #----------------------------------------------------------------------------- |
|
|||
26 | # Class |
|
|||
27 | #----------------------------------------------------------------------------- |
|
|||
28 |
|
16 | |||
29 | class TestExport(ExportersTestsBase): |
|
17 | class TestExport(ExportersTestsBase): | |
30 | """Contains test functions for export.py""" |
|
18 | """Contains test functions for export.py""" | |
31 |
|
19 | |||
32 |
|
20 | |||
33 | def test_export_wrong_name(self): |
|
21 | def test_export_wrong_name(self): | |
34 | """ |
|
22 | """ | |
35 | Is the right error thrown when a bad template name is used? |
|
23 | Is the right error thrown when a bad template name is used? | |
36 | """ |
|
24 | """ | |
37 | try: |
|
25 | try: | |
38 | export_by_name('not_a_name', self._get_notebook()) |
|
26 | export_by_name('not_a_name', self._get_notebook()) | |
39 | except ExporterNameError as e: |
|
27 | except ExporterNameError as e: | |
40 | pass |
|
28 | pass | |
41 |
|
29 | |||
42 |
|
30 | |||
43 | def test_export_filename(self): |
|
31 | def test_export_filename(self): | |
44 | """ |
|
32 | """ | |
45 | Can a notebook be exported by filename? |
|
33 | Can a notebook be exported by filename? | |
46 | """ |
|
34 | """ | |
47 | (output, resources) = export_by_name('python', self._get_notebook()) |
|
35 | (output, resources) = export_by_name('python', self._get_notebook()) | |
48 | assert len(output) > 0 |
|
36 | assert len(output) > 0 | |
49 |
|
37 | |||
50 |
|
38 | |||
51 | def test_export_nbnode(self): |
|
39 | def test_export_nbnode(self): | |
52 | """ |
|
40 | """ | |
53 | Can a notebook be exported by a notebook node handle? |
|
41 | Can a notebook be exported by a notebook node handle? | |
54 | """ |
|
42 | """ | |
55 | with open(self._get_notebook(), 'r') as f: |
|
43 | with open(self._get_notebook(), 'r') as f: | |
56 |
notebook = nbformat.read(f, |
|
44 | notebook = nbformat.read(f, 4) | |
57 | (output, resources) = export_by_name('python', notebook) |
|
45 | (output, resources) = export_by_name('python', notebook) | |
58 | assert len(output) > 0 |
|
46 | assert len(output) > 0 | |
59 |
|
47 | |||
60 |
|
48 | |||
61 | def test_export_filestream(self): |
|
49 | def test_export_filestream(self): | |
62 | """ |
|
50 | """ | |
63 | Can a notebook be exported by a filesteam? |
|
51 | Can a notebook be exported by a filesteam? | |
64 | """ |
|
52 | """ | |
65 | with open(self._get_notebook(), 'r') as f: |
|
53 | with open(self._get_notebook(), 'r') as f: | |
66 | (output, resources) = export_by_name('python', f) |
|
54 | (output, resources) = export_by_name('python', f) | |
67 | assert len(output) > 0 |
|
55 | assert len(output) > 0 | |
68 |
|
56 | |||
69 |
|
57 | |||
70 | def test_export_using_exporter(self): |
|
58 | def test_export_using_exporter(self): | |
71 | """ |
|
59 | """ | |
72 | Can a notebook be exported using an instanciated exporter? |
|
60 | Can a notebook be exported using an instanciated exporter? | |
73 | """ |
|
61 | """ | |
74 | (output, resources) = export(PythonExporter(), self._get_notebook()) |
|
62 | (output, resources) = export(PythonExporter(), self._get_notebook()) | |
75 | assert len(output) > 0 |
|
63 | assert len(output) > 0 | |
76 |
|
64 | |||
77 |
|
65 | |||
78 | def test_export_using_exporter_class(self): |
|
66 | def test_export_using_exporter_class(self): | |
79 | """ |
|
67 | """ | |
80 | Can a notebook be exported using an exporter class type? |
|
68 | Can a notebook be exported using an exporter class type? | |
81 | """ |
|
69 | """ | |
82 | (output, resources) = export(PythonExporter, self._get_notebook()) |
|
70 | (output, resources) = export(PythonExporter, self._get_notebook()) | |
83 | assert len(output) > 0 |
|
71 | assert len(output) > 0 | |
84 |
|
72 | |||
85 |
|
73 | |||
86 | def test_export_resources(self): |
|
74 | def test_export_resources(self): | |
87 | """ |
|
75 | """ | |
88 | Can a notebook be exported along with a custom resources dict? |
|
76 | Can a notebook be exported along with a custom resources dict? | |
89 | """ |
|
77 | """ | |
90 | (output, resources) = export(PythonExporter, self._get_notebook(), resources={}) |
|
78 | (output, resources) = export(PythonExporter, self._get_notebook(), resources={}) | |
91 | assert len(output) > 0 |
|
79 | assert len(output) > 0 | |
92 |
|
80 | |||
93 |
|
81 | |||
94 | def test_no_exporter(self): |
|
82 | def test_no_exporter(self): | |
95 | """ |
|
83 | """ | |
96 | Is the right error thrown if no exporter is provided? |
|
84 | Is the right error thrown if no exporter is provided? | |
97 | """ |
|
85 | """ | |
98 | try: |
|
86 | try: | |
99 | (output, resources) = export(None, self._get_notebook()) |
|
87 | (output, resources) = export(None, self._get_notebook()) | |
100 | except TypeError: |
|
88 | except TypeError: | |
101 | pass |
|
89 | pass | |
102 |
|
90 |
@@ -1,116 +1,117 | |||||
1 | """Tests for Latex exporter""" |
|
1 | """Tests for Latex exporter""" | |
2 |
|
2 | |||
3 | # Copyright (c) IPython Development Team. |
|
3 | # Copyright (c) IPython Development Team. | |
4 | # Distributed under the terms of the Modified BSD License. |
|
4 | # Distributed under the terms of the Modified BSD License. | |
5 |
|
5 | |||
6 | import os.path |
|
6 | import os.path | |
7 | import textwrap |
|
7 | import textwrap | |
8 | import re |
|
8 | import re | |
9 |
|
9 | |||
10 | from .base import ExportersTestsBase |
|
10 | from .base import ExportersTestsBase | |
11 | from ..latex import LatexExporter |
|
11 | from ..latex import LatexExporter | |
12 |
from IPython.nbformat import |
|
12 | from IPython.nbformat import write | |
|
13 | from IPython.nbformat import v4 | |||
13 | from IPython.testing.decorators import onlyif_cmds_exist |
|
14 | from IPython.testing.decorators import onlyif_cmds_exist | |
14 | from IPython.utils.tempdir import TemporaryDirectory |
|
15 | from IPython.utils.tempdir import TemporaryDirectory | |
15 |
|
16 | |||
16 |
|
17 | |||
17 | class TestLatexExporter(ExportersTestsBase): |
|
18 | class TestLatexExporter(ExportersTestsBase): | |
18 | """Contains test functions for latex.py""" |
|
19 | """Contains test functions for latex.py""" | |
19 |
|
20 | |||
20 | exporter_class = LatexExporter |
|
21 | exporter_class = LatexExporter | |
21 | should_include_raw = ['latex'] |
|
22 | should_include_raw = ['latex'] | |
22 |
|
23 | |||
23 | def test_constructor(self): |
|
24 | def test_constructor(self): | |
24 | """ |
|
25 | """ | |
25 | Can a LatexExporter be constructed? |
|
26 | Can a LatexExporter be constructed? | |
26 | """ |
|
27 | """ | |
27 | LatexExporter() |
|
28 | LatexExporter() | |
28 |
|
29 | |||
29 |
|
30 | |||
30 | @onlyif_cmds_exist('pandoc') |
|
31 | @onlyif_cmds_exist('pandoc') | |
31 | def test_export(self): |
|
32 | def test_export(self): | |
32 | """ |
|
33 | """ | |
33 | Can a LatexExporter export something? |
|
34 | Can a LatexExporter export something? | |
34 | """ |
|
35 | """ | |
35 | (output, resources) = LatexExporter().from_filename(self._get_notebook()) |
|
36 | (output, resources) = LatexExporter().from_filename(self._get_notebook()) | |
36 | assert len(output) > 0 |
|
37 | assert len(output) > 0 | |
37 |
|
38 | |||
38 |
|
39 | |||
39 | @onlyif_cmds_exist('pandoc') |
|
40 | @onlyif_cmds_exist('pandoc') | |
40 | def test_export_book(self): |
|
41 | def test_export_book(self): | |
41 | """ |
|
42 | """ | |
42 | Can a LatexExporter export using 'report' template? |
|
43 | Can a LatexExporter export using 'report' template? | |
43 | """ |
|
44 | """ | |
44 | (output, resources) = LatexExporter(template_file='report').from_filename(self._get_notebook()) |
|
45 | (output, resources) = LatexExporter(template_file='report').from_filename(self._get_notebook()) | |
45 | assert len(output) > 0 |
|
46 | assert len(output) > 0 | |
46 |
|
47 | |||
47 |
|
48 | |||
48 | @onlyif_cmds_exist('pandoc') |
|
49 | @onlyif_cmds_exist('pandoc') | |
49 | def test_export_basic(self): |
|
50 | def test_export_basic(self): | |
50 | """ |
|
51 | """ | |
51 | Can a LatexExporter export using 'article' template? |
|
52 | Can a LatexExporter export using 'article' template? | |
52 | """ |
|
53 | """ | |
53 | (output, resources) = LatexExporter(template_file='article').from_filename(self._get_notebook()) |
|
54 | (output, resources) = LatexExporter(template_file='article').from_filename(self._get_notebook()) | |
54 | assert len(output) > 0 |
|
55 | assert len(output) > 0 | |
55 |
|
56 | |||
56 |
|
57 | |||
57 | @onlyif_cmds_exist('pandoc') |
|
58 | @onlyif_cmds_exist('pandoc') | |
58 | def test_export_article(self): |
|
59 | def test_export_article(self): | |
59 | """ |
|
60 | """ | |
60 | Can a LatexExporter export using 'article' template? |
|
61 | Can a LatexExporter export using 'article' template? | |
61 | """ |
|
62 | """ | |
62 | (output, resources) = LatexExporter(template_file='article').from_filename(self._get_notebook()) |
|
63 | (output, resources) = LatexExporter(template_file='article').from_filename(self._get_notebook()) | |
63 | assert len(output) > 0 |
|
64 | assert len(output) > 0 | |
64 |
|
65 | |||
65 | @onlyif_cmds_exist('pandoc') |
|
66 | @onlyif_cmds_exist('pandoc') | |
66 | def test_very_long_cells(self): |
|
67 | def test_very_long_cells(self): | |
67 | """ |
|
68 | """ | |
68 | Torture test that long cells do not cause issues |
|
69 | Torture test that long cells do not cause issues | |
69 | """ |
|
70 | """ | |
70 | lorem_ipsum_text = textwrap.dedent("""\ |
|
71 | lorem_ipsum_text = textwrap.dedent("""\ | |
71 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec |
|
72 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec | |
72 | dignissim, ipsum non facilisis tempus, dui felis tincidunt metus, |
|
73 | dignissim, ipsum non facilisis tempus, dui felis tincidunt metus, | |
73 | nec pulvinar neque odio eget risus. Nulla nisi lectus, cursus |
|
74 | nec pulvinar neque odio eget risus. Nulla nisi lectus, cursus | |
74 | suscipit interdum at, ultrices sit amet orci. Mauris facilisis |
|
75 | suscipit interdum at, ultrices sit amet orci. Mauris facilisis | |
75 | imperdiet elit, vitae scelerisque ipsum dignissim non. Integer |
|
76 | imperdiet elit, vitae scelerisque ipsum dignissim non. Integer | |
76 | consequat malesuada neque sit amet pulvinar. Curabitur pretium |
|
77 | consequat malesuada neque sit amet pulvinar. Curabitur pretium | |
77 | ut turpis eget aliquet. Maecenas sagittis lacus sed lectus |
|
78 | ut turpis eget aliquet. Maecenas sagittis lacus sed lectus | |
78 | volutpat, eu adipiscing purus pulvinar. Maecenas consequat |
|
79 | volutpat, eu adipiscing purus pulvinar. Maecenas consequat | |
79 | luctus urna, eget cursus quam mollis a. Aliquam vitae ornare |
|
80 | luctus urna, eget cursus quam mollis a. Aliquam vitae ornare | |
80 | erat, non hendrerit urna. Sed eu diam nec massa egestas pharetra |
|
81 | erat, non hendrerit urna. Sed eu diam nec massa egestas pharetra | |
81 | at nec tellus. Fusce feugiat lacus quis urna sollicitudin volutpat. |
|
82 | at nec tellus. Fusce feugiat lacus quis urna sollicitudin volutpat. | |
82 | Quisque at sapien non nibh feugiat tempus ac ultricies purus. |
|
83 | Quisque at sapien non nibh feugiat tempus ac ultricies purus. | |
83 | """) |
|
84 | """) | |
84 | lorem_ipsum_text = lorem_ipsum_text.replace("\n"," ") + "\n\n" |
|
85 | lorem_ipsum_text = lorem_ipsum_text.replace("\n"," ") + "\n\n" | |
85 | large_lorem_ipsum_text = "".join([lorem_ipsum_text]*3000) |
|
86 | large_lorem_ipsum_text = "".join([lorem_ipsum_text]*3000) | |
86 |
|
87 | |||
87 | notebook_name = "lorem_ipsum_long.ipynb" |
|
88 | notebook_name = "lorem_ipsum_long.ipynb" | |
88 |
nb = |
|
89 | nb = v4.new_notebook( | |
89 | cells=[ |
|
90 | cells=[ | |
90 |
|
|
91 | v4.new_markdown_cell(source=large_lorem_ipsum_text) | |
91 | ] |
|
92 | ] | |
92 | ) |
|
93 | ) | |
93 |
|
94 | |||
94 | with TemporaryDirectory() as td: |
|
95 | with TemporaryDirectory() as td: | |
95 | nbfile = os.path.join(td, notebook_name) |
|
96 | nbfile = os.path.join(td, notebook_name) | |
96 | with open(nbfile, 'w') as f: |
|
97 | with open(nbfile, 'w') as f: | |
97 |
|
|
98 | write(f, nb, 4) | |
98 |
|
99 | |||
99 |
(output, resources) = LatexExporter(template_file='article').from_filename(nbfile) |
|
100 | (output, resources) = LatexExporter(template_file='article').from_filename(nbfile) | |
100 | assert len(output) > 0 |
|
101 | assert len(output) > 0 | |
101 |
|
102 | |||
102 | @onlyif_cmds_exist('pandoc') |
|
103 | @onlyif_cmds_exist('pandoc') | |
103 | def test_prompt_number_color(self): |
|
104 | def test_prompt_number_color(self): | |
104 | """ |
|
105 | """ | |
105 | Does LatexExporter properly format input and output prompts in color? |
|
106 | Does LatexExporter properly format input and output prompts in color? | |
106 | """ |
|
107 | """ | |
107 | (output, resources) = LatexExporter().from_filename( |
|
108 | (output, resources) = LatexExporter().from_filename( | |
108 | self._get_notebook(nb_name="prompt_numbers.ipynb")) |
|
109 | self._get_notebook(nb_name="prompt_numbers.ipynb")) | |
109 | in_regex = r"In \[\{\\color\{incolor\}(.*)\}\]:" |
|
110 | in_regex = r"In \[\{\\color\{incolor\}(.*)\}\]:" | |
110 | out_regex = r"Out\[\{\\color\{outcolor\}(.*)\}\]:" |
|
111 | out_regex = r"Out\[\{\\color\{outcolor\}(.*)\}\]:" | |
111 |
|
112 | |||
112 | ins = ["2", "10", " ", " ", "*", "0"] |
|
113 | ins = ["2", "10", " ", " ", "*", "0"] | |
113 | outs = ["10"] |
|
114 | outs = ["10"] | |
114 |
|
115 | |||
115 | assert re.findall(in_regex, output) == ins |
|
116 | assert re.findall(in_regex, output) == ins | |
116 | assert re.findall(out_regex, output) == outs |
|
117 | assert re.findall(out_regex, output) == outs |
@@ -1,39 +1,39 | |||||
1 | """Tests for notebook.py""" |
|
1 | """Tests for notebook.py""" | |
2 |
|
2 | |||
3 | # Copyright (c) IPython Development Team. |
|
3 | # Copyright (c) IPython Development Team. | |
4 | # Distributed under the terms of the Modified BSD License. |
|
4 | # Distributed under the terms of the Modified BSD License. | |
5 |
|
5 | |||
6 | import json |
|
6 | import json | |
7 |
|
7 | |||
8 | from .base import ExportersTestsBase |
|
8 | from .base import ExportersTestsBase | |
9 | from ..notebook import NotebookExporter |
|
9 | from ..notebook import NotebookExporter | |
10 |
|
10 | |||
11 |
from IPython.nbformat |
|
11 | from IPython.nbformat import validate | |
12 | from IPython.testing.tools import assert_big_text_equal |
|
12 | from IPython.testing.tools import assert_big_text_equal | |
13 |
|
13 | |||
14 | class TestNotebookExporter(ExportersTestsBase): |
|
14 | class TestNotebookExporter(ExportersTestsBase): | |
15 | """Contains test functions for notebook.py""" |
|
15 | """Contains test functions for notebook.py""" | |
16 |
|
16 | |||
17 | exporter_class = NotebookExporter |
|
17 | exporter_class = NotebookExporter | |
18 |
|
18 | |||
19 | def test_export(self): |
|
19 | def test_export(self): | |
20 | """ |
|
20 | """ | |
21 | Does the NotebookExporter return the file unchanged? |
|
21 | Does the NotebookExporter return the file unchanged? | |
22 | """ |
|
22 | """ | |
23 | with open(self._get_notebook()) as f: |
|
23 | with open(self._get_notebook()) as f: | |
24 | file_contents = f.read() |
|
24 | file_contents = f.read() | |
25 | (output, resources) = self.exporter_class().from_filename(self._get_notebook()) |
|
25 | (output, resources) = self.exporter_class().from_filename(self._get_notebook()) | |
26 | assert len(output) > 0 |
|
26 | assert len(output) > 0 | |
27 | assert_big_text_equal(output, file_contents) |
|
27 | assert_big_text_equal(output, file_contents) | |
28 |
|
28 | |||
29 | def test_downgrade_3(self): |
|
29 | def test_downgrade_3(self): | |
30 | exporter = self.exporter_class(nbformat_version=3) |
|
30 | exporter = self.exporter_class(nbformat_version=3) | |
31 | (output, resources) = exporter.from_filename(self._get_notebook()) |
|
31 | (output, resources) = exporter.from_filename(self._get_notebook()) | |
32 | nb = json.loads(output) |
|
32 | nb = json.loads(output) | |
33 | validate(nb) |
|
33 | validate(nb) | |
34 |
|
34 | |||
35 | def test_downgrade_2(self): |
|
35 | def test_downgrade_2(self): | |
36 | exporter = self.exporter_class(nbformat_version=2) |
|
36 | exporter = self.exporter_class(nbformat_version=2) | |
37 | (output, resources) = exporter.from_filename(self._get_notebook()) |
|
37 | (output, resources) = exporter.from_filename(self._get_notebook()) | |
38 | nb = json.loads(output) |
|
38 | nb = json.loads(output) | |
39 | self.assertEqual(nb['nbformat'], 2) |
|
39 | self.assertEqual(nb['nbformat'], 2) |
@@ -1,62 +1,54 | |||||
1 | """Tests for RSTExporter""" |
|
1 | """Tests for RSTExporter""" | |
2 |
|
2 | |||
3 | #----------------------------------------------------------------------------- |
|
3 | # Copyright (c) IPython Development Team. | |
4 | # Copyright (c) 2013, the IPython Development Team. |
|
|||
5 | # |
|
|||
6 | # Distributed under the terms of the Modified BSD License. |
|
4 | # Distributed under the terms of the Modified BSD License. | |
7 | # |
|
|||
8 | # The full license is in the file COPYING.txt, distributed with this software. |
|
|||
9 | #----------------------------------------------------------------------------- |
|
|||
10 |
|
||||
11 | #----------------------------------------------------------------------------- |
|
|||
12 | # Imports |
|
|||
13 | #----------------------------------------------------------------------------- |
|
|||
14 |
|
5 | |||
15 | import io |
|
6 | import io | |
16 |
|
7 | |||
17 |
from IPython |
|
8 | from IPython import nbformat | |
|
9 | from IPython.nbformat import v4 | |||
18 |
|
10 | |||
19 | from .base import ExportersTestsBase |
|
11 | from .base import ExportersTestsBase | |
20 | from ..rst import RSTExporter |
|
12 | from ..rst import RSTExporter | |
21 | from IPython.testing.decorators import onlyif_cmds_exist |
|
13 | from IPython.testing.decorators import onlyif_cmds_exist | |
22 |
|
14 | |||
23 |
|
15 | |||
24 | class TestRSTExporter(ExportersTestsBase): |
|
16 | class TestRSTExporter(ExportersTestsBase): | |
25 | """Tests for RSTExporter""" |
|
17 | """Tests for RSTExporter""" | |
26 |
|
18 | |||
27 | exporter_class = RSTExporter |
|
19 | exporter_class = RSTExporter | |
28 | should_include_raw = ['rst'] |
|
20 | should_include_raw = ['rst'] | |
29 |
|
21 | |||
30 | def test_constructor(self): |
|
22 | def test_constructor(self): | |
31 | """ |
|
23 | """ | |
32 | Can a RSTExporter be constructed? |
|
24 | Can a RSTExporter be constructed? | |
33 | """ |
|
25 | """ | |
34 | RSTExporter() |
|
26 | RSTExporter() | |
35 |
|
27 | |||
36 |
|
28 | |||
37 | @onlyif_cmds_exist('pandoc') |
|
29 | @onlyif_cmds_exist('pandoc') | |
38 | def test_export(self): |
|
30 | def test_export(self): | |
39 | """ |
|
31 | """ | |
40 | Can a RSTExporter export something? |
|
32 | Can a RSTExporter export something? | |
41 | """ |
|
33 | """ | |
42 | (output, resources) = RSTExporter().from_filename(self._get_notebook()) |
|
34 | (output, resources) = RSTExporter().from_filename(self._get_notebook()) | |
43 | assert len(output) > 0 |
|
35 | assert len(output) > 0 | |
44 |
|
36 | |||
45 | @onlyif_cmds_exist('pandoc') |
|
37 | @onlyif_cmds_exist('pandoc') | |
46 | def test_empty_code_cell(self): |
|
38 | def test_empty_code_cell(self): | |
47 | """No empty code cells in rst""" |
|
39 | """No empty code cells in rst""" | |
48 | nbname = self._get_notebook() |
|
40 | nbname = self._get_notebook() | |
49 | with io.open(nbname, encoding='utf8') as f: |
|
41 | with io.open(nbname, encoding='utf8') as f: | |
50 |
nb = |
|
42 | nb = nbformat.read(f, 4) | |
51 |
|
43 | |||
52 | exporter = self.exporter_class() |
|
44 | exporter = self.exporter_class() | |
53 |
|
45 | |||
54 | (output, resources) = exporter.from_notebook_node(nb) |
|
46 | (output, resources) = exporter.from_notebook_node(nb) | |
55 | # add an empty code cell |
|
47 | # add an empty code cell | |
56 | nb.cells.append( |
|
48 | nb.cells.append( | |
57 |
|
|
49 | v4.new_code_cell(source="") | |
58 | ) |
|
50 | ) | |
59 | (output2, resources) = exporter.from_notebook_node(nb) |
|
51 | (output2, resources) = exporter.from_notebook_node(nb) | |
60 | # adding an empty code cell shouldn't change output |
|
52 | # adding an empty code cell shouldn't change output | |
61 | self.assertEqual(output.strip(), output2.strip()) |
|
53 | self.assertEqual(output.strip(), output2.strip()) | |
62 |
|
54 |
@@ -1,111 +1,111 | |||||
1 | """Module containing a preprocessor that removes the outputs from code cells""" |
|
1 | """Module containing a preprocessor that removes the outputs from code cells""" | |
2 |
|
2 | |||
3 | # Copyright (c) IPython Development Team. |
|
3 | # Copyright (c) IPython Development Team. | |
4 | # Distributed under the terms of the Modified BSD License. |
|
4 | # Distributed under the terms of the Modified BSD License. | |
5 |
|
5 | |||
6 | import os |
|
6 | import os | |
7 | import sys |
|
|||
8 |
|
7 | |||
9 | try: |
|
8 | try: | |
10 | from queue import Empty # Py 3 |
|
9 | from queue import Empty # Py 3 | |
11 | except ImportError: |
|
10 | except ImportError: | |
12 | from Queue import Empty # Py 2 |
|
11 | from Queue import Empty # Py 2 | |
13 |
|
12 | |||
14 | from IPython.utils.traitlets import List, Unicode |
|
13 | from IPython.utils.traitlets import List, Unicode | |
15 |
|
14 | |||
16 |
from IPython.nbformat. |
|
15 | from IPython.nbformat.v4 import output_from_msg | |
17 | from .base import Preprocessor |
|
16 | from .base import Preprocessor | |
18 | from IPython.utils.traitlets import Integer |
|
17 | from IPython.utils.traitlets import Integer | |
19 |
|
18 | |||
|
19 | ||||
20 | class ExecutePreprocessor(Preprocessor): |
|
20 | class ExecutePreprocessor(Preprocessor): | |
21 | """ |
|
21 | """ | |
22 | Executes all the cells in a notebook |
|
22 | Executes all the cells in a notebook | |
23 | """ |
|
23 | """ | |
24 |
|
24 | |||
25 | timeout = Integer(30, config=True, |
|
25 | timeout = Integer(30, config=True, | |
26 | help="The time to wait (in seconds) for output from executions." |
|
26 | help="The time to wait (in seconds) for output from executions." | |
27 | ) |
|
27 | ) | |
28 |
|
28 | |||
29 | extra_arguments = List(Unicode) |
|
29 | extra_arguments = List(Unicode) | |
30 |
|
30 | |||
31 | def preprocess(self, nb, resources): |
|
31 | def preprocess(self, nb, resources): | |
32 | from IPython.kernel import run_kernel |
|
32 | from IPython.kernel import run_kernel | |
33 | kernel_name = nb.metadata.get('kernelspec', {}).get('name', 'python') |
|
33 | kernel_name = nb.metadata.get('kernelspec', {}).get('name', 'python') | |
34 | self.log.info("Executing notebook with kernel: %s" % kernel_name) |
|
34 | self.log.info("Executing notebook with kernel: %s" % kernel_name) | |
35 | with run_kernel(kernel_name=kernel_name, |
|
35 | with run_kernel(kernel_name=kernel_name, | |
36 | extra_arguments=self.extra_arguments, |
|
36 | extra_arguments=self.extra_arguments, | |
37 | stderr=open(os.devnull, 'w')) as kc: |
|
37 | stderr=open(os.devnull, 'w')) as kc: | |
38 | self.kc = kc |
|
38 | self.kc = kc | |
39 | nb, resources = super(ExecutePreprocessor, self).preprocess(nb, resources) |
|
39 | nb, resources = super(ExecutePreprocessor, self).preprocess(nb, resources) | |
40 | return nb, resources |
|
40 | return nb, resources | |
41 |
|
41 | |||
42 | def preprocess_cell(self, cell, resources, cell_index): |
|
42 | def preprocess_cell(self, cell, resources, cell_index): | |
43 | """ |
|
43 | """ | |
44 | Apply a transformation on each code cell. See base.py for details. |
|
44 | Apply a transformation on each code cell. See base.py for details. | |
45 | """ |
|
45 | """ | |
46 | if cell.cell_type != 'code': |
|
46 | if cell.cell_type != 'code': | |
47 | return cell, resources |
|
47 | return cell, resources | |
48 | try: |
|
48 | try: | |
49 | outputs = self.run_cell(self.kc.shell_channel, self.kc.iopub_channel, cell) |
|
49 | outputs = self.run_cell(self.kc.shell_channel, self.kc.iopub_channel, cell) | |
50 | except Exception as e: |
|
50 | except Exception as e: | |
51 | self.log.error("failed to run cell: " + repr(e)) |
|
51 | self.log.error("failed to run cell: " + repr(e)) | |
52 | self.log.error(str(cell.source)) |
|
52 | self.log.error(str(cell.source)) | |
53 | raise |
|
53 | raise | |
54 | cell.outputs = outputs |
|
54 | cell.outputs = outputs | |
55 | return cell, resources |
|
55 | return cell, resources | |
56 |
|
56 | |||
57 | def run_cell(self, shell, iopub, cell): |
|
57 | def run_cell(self, shell, iopub, cell): | |
58 | msg_id = shell.execute(cell.source) |
|
58 | msg_id = shell.execute(cell.source) | |
59 | self.log.debug("Executing cell:\n%s", cell.source) |
|
59 | self.log.debug("Executing cell:\n%s", cell.source) | |
60 | # wait for finish, with timeout |
|
60 | # wait for finish, with timeout | |
61 | while True: |
|
61 | while True: | |
62 | try: |
|
62 | try: | |
63 | msg = shell.get_msg(timeout=self.timeout) |
|
63 | msg = shell.get_msg(timeout=self.timeout) | |
64 | except Empty: |
|
64 | except Empty: | |
65 | self.log.error("Timeout waiting for execute reply") |
|
65 | self.log.error("Timeout waiting for execute reply") | |
66 | raise |
|
66 | raise | |
67 | if msg['parent_header'].get('msg_id') == msg_id: |
|
67 | if msg['parent_header'].get('msg_id') == msg_id: | |
68 | break |
|
68 | break | |
69 | else: |
|
69 | else: | |
70 | # not our reply |
|
70 | # not our reply | |
71 | continue |
|
71 | continue | |
72 |
|
72 | |||
73 | outs = [] |
|
73 | outs = [] | |
74 |
|
74 | |||
75 | while True: |
|
75 | while True: | |
76 | try: |
|
76 | try: | |
77 | msg = iopub.get_msg(timeout=self.timeout) |
|
77 | msg = iopub.get_msg(timeout=self.timeout) | |
78 | except Empty: |
|
78 | except Empty: | |
79 | self.log.warn("Timeout waiting for IOPub output") |
|
79 | self.log.warn("Timeout waiting for IOPub output") | |
80 | break |
|
80 | break | |
81 | if msg['parent_header'].get('msg_id') != msg_id: |
|
81 | if msg['parent_header'].get('msg_id') != msg_id: | |
82 | # not an output from our execution |
|
82 | # not an output from our execution | |
83 | continue |
|
83 | continue | |
84 |
|
84 | |||
85 | msg_type = msg['msg_type'] |
|
85 | msg_type = msg['msg_type'] | |
86 | self.log.debug("output: %s", msg_type) |
|
86 | self.log.debug("output: %s", msg_type) | |
87 | content = msg['content'] |
|
87 | content = msg['content'] | |
88 |
|
88 | |||
89 | # set the prompt number for the input and the output |
|
89 | # set the prompt number for the input and the output | |
90 | if 'execution_count' in content: |
|
90 | if 'execution_count' in content: | |
91 | cell['execution_count'] = content['execution_count'] |
|
91 | cell['execution_count'] = content['execution_count'] | |
92 |
|
92 | |||
93 | if msg_type == 'status': |
|
93 | if msg_type == 'status': | |
94 | if content['execution_state'] == 'idle': |
|
94 | if content['execution_state'] == 'idle': | |
95 | break |
|
95 | break | |
96 | else: |
|
96 | else: | |
97 | continue |
|
97 | continue | |
98 | elif msg_type == 'execute_input': |
|
98 | elif msg_type == 'execute_input': | |
99 | continue |
|
99 | continue | |
100 | elif msg_type == 'clear_output': |
|
100 | elif msg_type == 'clear_output': | |
101 | outs = [] |
|
101 | outs = [] | |
102 | continue |
|
102 | continue | |
103 |
|
103 | |||
104 | try: |
|
104 | try: | |
105 | out = output_from_msg(msg) |
|
105 | out = output_from_msg(msg) | |
106 | except ValueError: |
|
106 | except ValueError: | |
107 | self.log.error("unhandled iopub msg: " + msg_type) |
|
107 | self.log.error("unhandled iopub msg: " + msg_type) | |
108 | else: |
|
108 | else: | |
109 | outs.append(out) |
|
109 | outs.append(out) | |
110 |
|
110 | |||
111 | return outs |
|
111 | return outs |
@@ -1,41 +1,41 | |||||
1 | """utility functions for preprocessor tests""" |
|
1 | """utility functions for preprocessor tests""" | |
2 |
|
2 | |||
3 | # Copyright (c) IPython Development Team. |
|
3 | # Copyright (c) IPython Development Team. | |
4 | # Distributed under the terms of the Modified BSD License. |
|
4 | # Distributed under the terms of the Modified BSD License. | |
5 |
|
5 | |||
6 |
from IPython.nbformat import |
|
6 | from IPython.nbformat import v4 as nbformat | |
7 |
|
7 | |||
8 | from ...tests.base import TestsBase |
|
8 | from ...tests.base import TestsBase | |
9 | from ...exporters.exporter import ResourcesDict |
|
9 | from ...exporters.exporter import ResourcesDict | |
10 |
|
10 | |||
11 |
|
11 | |||
12 | class PreprocessorTestsBase(TestsBase): |
|
12 | class PreprocessorTestsBase(TestsBase): | |
13 | """Contains test functions preprocessor tests""" |
|
13 | """Contains test functions preprocessor tests""" | |
14 |
|
14 | |||
15 |
|
15 | |||
16 | def build_notebook(self): |
|
16 | def build_notebook(self): | |
17 | """Build a notebook in memory for use with preprocessor tests""" |
|
17 | """Build a notebook in memory for use with preprocessor tests""" | |
18 |
|
18 | |||
19 | outputs = [ |
|
19 | outputs = [ | |
20 | nbformat.new_output("stream", name="stdout", text="a"), |
|
20 | nbformat.new_output("stream", name="stdout", text="a"), | |
21 | nbformat.new_output("display_data", data={'text/plain': 'b'}), |
|
21 | nbformat.new_output("display_data", data={'text/plain': 'b'}), | |
22 | nbformat.new_output("stream", name="stdout", text="c"), |
|
22 | nbformat.new_output("stream", name="stdout", text="c"), | |
23 | nbformat.new_output("stream", name="stdout", text="d"), |
|
23 | nbformat.new_output("stream", name="stdout", text="d"), | |
24 | nbformat.new_output("stream", name="stderr", text="e"), |
|
24 | nbformat.new_output("stream", name="stderr", text="e"), | |
25 | nbformat.new_output("stream", name="stderr", text="f"), |
|
25 | nbformat.new_output("stream", name="stderr", text="f"), | |
26 | nbformat.new_output("display_data", data={'image/png': 'Zw=='}), # g |
|
26 | nbformat.new_output("display_data", data={'image/png': 'Zw=='}), # g | |
27 | nbformat.new_output("display_data", data={'application/pdf': 'aA=='}), # h |
|
27 | nbformat.new_output("display_data", data={'application/pdf': 'aA=='}), # h | |
28 | ] |
|
28 | ] | |
29 |
|
29 | |||
30 | cells=[nbformat.new_code_cell(source="$ e $", execution_count=1, outputs=outputs), |
|
30 | cells=[nbformat.new_code_cell(source="$ e $", execution_count=1, outputs=outputs), | |
31 | nbformat.new_markdown_cell(source="$ e $")] |
|
31 | nbformat.new_markdown_cell(source="$ e $")] | |
32 |
|
32 | |||
33 | return nbformat.new_notebook(cells=cells) |
|
33 | return nbformat.new_notebook(cells=cells) | |
34 |
|
34 | |||
35 |
|
35 | |||
36 | def build_resources(self): |
|
36 | def build_resources(self): | |
37 | """Build an empty resources dictionary.""" |
|
37 | """Build an empty resources dictionary.""" | |
38 |
|
38 | |||
39 | res = ResourcesDict() |
|
39 | res = ResourcesDict() | |
40 | res['metadata'] = ResourcesDict() |
|
40 | res['metadata'] = ResourcesDict() | |
41 | return res |
|
41 | return res |
@@ -1,35 +1,33 | |||||
1 | """ |
|
1 | """ | |
2 | Module with tests for the clearoutput preprocessor. |
|
2 | Module with tests for the clearoutput preprocessor. | |
3 | """ |
|
3 | """ | |
4 |
|
4 | |||
5 | # Copyright (c) IPython Development Team. |
|
5 | # Copyright (c) IPython Development Team. | |
6 | # Distributed under the terms of the Modified BSD License. |
|
6 | # Distributed under the terms of the Modified BSD License. | |
7 |
|
7 | |||
8 | from IPython.nbformat import current as nbformat |
|
|||
9 |
|
||||
10 | from .base import PreprocessorTestsBase |
|
8 | from .base import PreprocessorTestsBase | |
11 | from ..clearoutput import ClearOutputPreprocessor |
|
9 | from ..clearoutput import ClearOutputPreprocessor | |
12 |
|
10 | |||
13 |
|
11 | |||
14 | class TestClearOutput(PreprocessorTestsBase): |
|
12 | class TestClearOutput(PreprocessorTestsBase): | |
15 | """Contains test functions for clearoutput.py""" |
|
13 | """Contains test functions for clearoutput.py""" | |
16 |
|
14 | |||
17 |
|
15 | |||
18 | def build_preprocessor(self): |
|
16 | def build_preprocessor(self): | |
19 | """Make an instance of a preprocessor""" |
|
17 | """Make an instance of a preprocessor""" | |
20 | preprocessor = ClearOutputPreprocessor() |
|
18 | preprocessor = ClearOutputPreprocessor() | |
21 | preprocessor.enabled = True |
|
19 | preprocessor.enabled = True | |
22 | return preprocessor |
|
20 | return preprocessor | |
23 |
|
21 | |||
24 | def test_constructor(self): |
|
22 | def test_constructor(self): | |
25 | """Can a ClearOutputPreprocessor be constructed?""" |
|
23 | """Can a ClearOutputPreprocessor be constructed?""" | |
26 | self.build_preprocessor() |
|
24 | self.build_preprocessor() | |
27 |
|
25 | |||
28 | def test_output(self): |
|
26 | def test_output(self): | |
29 | """Test the output of the ClearOutputPreprocessor""" |
|
27 | """Test the output of the ClearOutputPreprocessor""" | |
30 | nb = self.build_notebook() |
|
28 | nb = self.build_notebook() | |
31 | res = self.build_resources() |
|
29 | res = self.build_resources() | |
32 | preprocessor = self.build_preprocessor() |
|
30 | preprocessor = self.build_preprocessor() | |
33 | nb, res = preprocessor(nb, res) |
|
31 | nb, res = preprocessor(nb, res) | |
34 | assert nb.cells[0].outputs == [] |
|
32 | assert nb.cells[0].outputs == [] | |
35 | assert nb.cells[0].execution_count is None |
|
33 | assert nb.cells[0].execution_count is None |
@@ -1,58 +1,58 | |||||
1 | """Tests for the coalescestreams preprocessor""" |
|
1 | """Tests for the coalescestreams preprocessor""" | |
2 |
|
2 | |||
3 | # Copyright (c) IPython Development Team. |
|
3 | # Copyright (c) IPython Development Team. | |
4 | # Distributed under the terms of the Modified BSD License. |
|
4 | # Distributed under the terms of the Modified BSD License. | |
5 |
|
5 | |||
6 |
from IPython.nbformat import |
|
6 | from IPython.nbformat import v4 as nbformat | |
7 |
|
7 | |||
8 | from .base import PreprocessorTestsBase |
|
8 | from .base import PreprocessorTestsBase | |
9 | from ..coalescestreams import coalesce_streams |
|
9 | from ..coalescestreams import coalesce_streams | |
10 |
|
10 | |||
11 |
|
11 | |||
12 | class TestCoalesceStreams(PreprocessorTestsBase): |
|
12 | class TestCoalesceStreams(PreprocessorTestsBase): | |
13 | """Contains test functions for coalescestreams.py""" |
|
13 | """Contains test functions for coalescestreams.py""" | |
14 |
|
14 | |||
15 | def test_coalesce_streams(self): |
|
15 | def test_coalesce_streams(self): | |
16 | """coalesce_streams preprocessor output test""" |
|
16 | """coalesce_streams preprocessor output test""" | |
17 | nb = self.build_notebook() |
|
17 | nb = self.build_notebook() | |
18 | res = self.build_resources() |
|
18 | res = self.build_resources() | |
19 | nb, res = coalesce_streams(nb, res) |
|
19 | nb, res = coalesce_streams(nb, res) | |
20 | outputs = nb.cells[0].outputs |
|
20 | outputs = nb.cells[0].outputs | |
21 | self.assertEqual(outputs[0].text, "a") |
|
21 | self.assertEqual(outputs[0].text, "a") | |
22 | self.assertEqual(outputs[1].output_type, "display_data") |
|
22 | self.assertEqual(outputs[1].output_type, "display_data") | |
23 | self.assertEqual(outputs[2].text, "cd") |
|
23 | self.assertEqual(outputs[2].text, "cd") | |
24 | self.assertEqual(outputs[3].text, "ef") |
|
24 | self.assertEqual(outputs[3].text, "ef") | |
25 |
|
25 | |||
26 | def test_coalesce_sequenced_streams(self): |
|
26 | def test_coalesce_sequenced_streams(self): | |
27 | """Can the coalesce streams preprocessor merge a sequence of streams?""" |
|
27 | """Can the coalesce streams preprocessor merge a sequence of streams?""" | |
28 | outputs = [nbformat.new_output(output_type="stream", name="stdout", text="0"), |
|
28 | outputs = [nbformat.new_output(output_type="stream", name="stdout", text="0"), | |
29 | nbformat.new_output(output_type="stream", name="stdout", text="1"), |
|
29 | nbformat.new_output(output_type="stream", name="stdout", text="1"), | |
30 | nbformat.new_output(output_type="stream", name="stdout", text="2"), |
|
30 | nbformat.new_output(output_type="stream", name="stdout", text="2"), | |
31 | nbformat.new_output(output_type="stream", name="stdout", text="3"), |
|
31 | nbformat.new_output(output_type="stream", name="stdout", text="3"), | |
32 | nbformat.new_output(output_type="stream", name="stdout", text="4"), |
|
32 | nbformat.new_output(output_type="stream", name="stdout", text="4"), | |
33 | nbformat.new_output(output_type="stream", name="stdout", text="5"), |
|
33 | nbformat.new_output(output_type="stream", name="stdout", text="5"), | |
34 | nbformat.new_output(output_type="stream", name="stdout", text="6"), |
|
34 | nbformat.new_output(output_type="stream", name="stdout", text="6"), | |
35 | nbformat.new_output(output_type="stream", name="stdout", text="7")] |
|
35 | nbformat.new_output(output_type="stream", name="stdout", text="7")] | |
36 | cells=[nbformat.new_code_cell(source="# None", execution_count=1,outputs=outputs)] |
|
36 | cells=[nbformat.new_code_cell(source="# None", execution_count=1,outputs=outputs)] | |
37 |
|
37 | |||
38 | nb = nbformat.new_notebook(cells=cells) |
|
38 | nb = nbformat.new_notebook(cells=cells) | |
39 | res = self.build_resources() |
|
39 | res = self.build_resources() | |
40 | nb, res = coalesce_streams(nb, res) |
|
40 | nb, res = coalesce_streams(nb, res) | |
41 | outputs = nb.cells[0].outputs |
|
41 | outputs = nb.cells[0].outputs | |
42 | self.assertEqual(outputs[0].text, u'01234567') |
|
42 | self.assertEqual(outputs[0].text, u'01234567') | |
43 |
|
43 | |||
44 | def test_coalesce_replace_streams(self): |
|
44 | def test_coalesce_replace_streams(self): | |
45 | """Are \\r characters handled?""" |
|
45 | """Are \\r characters handled?""" | |
46 | outputs = [nbformat.new_output(output_type="stream", name="stdout", text="z"), |
|
46 | outputs = [nbformat.new_output(output_type="stream", name="stdout", text="z"), | |
47 | nbformat.new_output(output_type="stream", name="stdout", text="\ra"), |
|
47 | nbformat.new_output(output_type="stream", name="stdout", text="\ra"), | |
48 | nbformat.new_output(output_type="stream", name="stdout", text="\nz\rb"), |
|
48 | nbformat.new_output(output_type="stream", name="stdout", text="\nz\rb"), | |
49 | nbformat.new_output(output_type="stream", name="stdout", text="\nz"), |
|
49 | nbformat.new_output(output_type="stream", name="stdout", text="\nz"), | |
50 | nbformat.new_output(output_type="stream", name="stdout", text="\rc\n"), |
|
50 | nbformat.new_output(output_type="stream", name="stdout", text="\rc\n"), | |
51 | nbformat.new_output(output_type="stream", name="stdout", text="z\rz\rd")] |
|
51 | nbformat.new_output(output_type="stream", name="stdout", text="z\rz\rd")] | |
52 | cells=[nbformat.new_code_cell(source="# None", execution_count=1,outputs=outputs)] |
|
52 | cells=[nbformat.new_code_cell(source="# None", execution_count=1,outputs=outputs)] | |
53 |
|
53 | |||
54 | nb = nbformat.new_notebook(cells=cells) |
|
54 | nb = nbformat.new_notebook(cells=cells) | |
55 | res = self.build_resources() |
|
55 | res = self.build_resources() | |
56 | nb, res = coalesce_streams(nb, res) |
|
56 | nb, res = coalesce_streams(nb, res) | |
57 | outputs = nb.cells[0].outputs |
|
57 | outputs = nb.cells[0].outputs | |
58 | self.assertEqual(outputs[0].text, u'a\nb\nc\nd') |
|
58 | self.assertEqual(outputs[0].text, u'a\nb\nc\nd') |
@@ -1,91 +1,92 | |||||
1 | """ |
|
1 | """ | |
2 | Module with tests for the execute preprocessor. |
|
2 | Module with tests for the execute preprocessor. | |
3 | """ |
|
3 | """ | |
4 |
|
4 | |||
5 | # Copyright (c) IPython Development Team. |
|
5 | # Copyright (c) IPython Development Team. | |
6 | # Distributed under the terms of the Modified BSD License. |
|
6 | # Distributed under the terms of the Modified BSD License. | |
7 |
|
7 | |||
8 | import copy |
|
8 | import copy | |
9 | import glob |
|
9 | import glob | |
|
10 | import io | |||
10 | import os |
|
11 | import os | |
11 | import re |
|
12 | import re | |
12 |
|
13 | |||
13 |
from IPython |
|
14 | from IPython import nbformat | |
14 |
|
15 | |||
15 | from .base import PreprocessorTestsBase |
|
16 | from .base import PreprocessorTestsBase | |
16 | from ..execute import ExecutePreprocessor |
|
17 | from ..execute import ExecutePreprocessor | |
17 |
|
18 | |||
18 | from IPython.nbconvert.filters import strip_ansi |
|
19 | from IPython.nbconvert.filters import strip_ansi | |
19 |
|
20 | |||
20 | addr_pat = re.compile(r'0x[0-9a-f]{7,9}') |
|
21 | addr_pat = re.compile(r'0x[0-9a-f]{7,9}') | |
21 |
|
22 | |||
22 | class TestExecute(PreprocessorTestsBase): |
|
23 | class TestExecute(PreprocessorTestsBase): | |
23 | """Contains test functions for execute.py""" |
|
24 | """Contains test functions for execute.py""" | |
24 |
|
25 | |||
25 | @staticmethod |
|
26 | @staticmethod | |
26 | def normalize_output(output): |
|
27 | def normalize_output(output): | |
27 | """ |
|
28 | """ | |
28 | Normalizes outputs for comparison. |
|
29 | Normalizes outputs for comparison. | |
29 | """ |
|
30 | """ | |
30 | output = dict(output) |
|
31 | output = dict(output) | |
31 | if 'metadata' in output: |
|
32 | if 'metadata' in output: | |
32 | del output['metadata'] |
|
33 | del output['metadata'] | |
33 | if 'text' in output: |
|
34 | if 'text' in output: | |
34 | output['text'] = re.sub(addr_pat, '<HEXADDR>', output['text']) |
|
35 | output['text'] = re.sub(addr_pat, '<HEXADDR>', output['text']) | |
35 | if 'text/plain' in output.get('data', {}): |
|
36 | if 'text/plain' in output.get('data', {}): | |
36 | output['data']['text/plain'] = \ |
|
37 | output['data']['text/plain'] = \ | |
37 | re.sub(addr_pat, '<HEXADDR>', output['data']['text/plain']) |
|
38 | re.sub(addr_pat, '<HEXADDR>', output['data']['text/plain']) | |
38 | if 'traceback' in output: |
|
39 | if 'traceback' in output: | |
39 | tb = [] |
|
40 | tb = [] | |
40 | for line in output['traceback']: |
|
41 | for line in output['traceback']: | |
41 | tb.append(strip_ansi(line)) |
|
42 | tb.append(strip_ansi(line)) | |
42 | output['traceback'] = tb |
|
43 | output['traceback'] = tb | |
43 |
|
44 | |||
44 | return output |
|
45 | return output | |
45 |
|
46 | |||
46 |
|
47 | |||
47 | def assert_notebooks_equal(self, expected, actual): |
|
48 | def assert_notebooks_equal(self, expected, actual): | |
48 | expected_cells = expected['cells'] |
|
49 | expected_cells = expected['cells'] | |
49 | actual_cells = actual['cells'] |
|
50 | actual_cells = actual['cells'] | |
50 | self.assertEqual(len(expected_cells), len(actual_cells)) |
|
51 | self.assertEqual(len(expected_cells), len(actual_cells)) | |
51 |
|
52 | |||
52 | for expected_cell, actual_cell in zip(expected_cells, actual_cells): |
|
53 | for expected_cell, actual_cell in zip(expected_cells, actual_cells): | |
53 | expected_outputs = expected_cell.get('outputs', []) |
|
54 | expected_outputs = expected_cell.get('outputs', []) | |
54 | actual_outputs = actual_cell.get('outputs', []) |
|
55 | actual_outputs = actual_cell.get('outputs', []) | |
55 | normalized_expected_outputs = list(map(self.normalize_output, expected_outputs)) |
|
56 | normalized_expected_outputs = list(map(self.normalize_output, expected_outputs)) | |
56 | normalized_actual_outputs = list(map(self.normalize_output, actual_outputs)) |
|
57 | normalized_actual_outputs = list(map(self.normalize_output, actual_outputs)) | |
57 | self.assertEqual(normalized_expected_outputs, normalized_actual_outputs) |
|
58 | self.assertEqual(normalized_expected_outputs, normalized_actual_outputs) | |
58 |
|
59 | |||
59 | expected_execution_count = expected_cell.get('execution_count', None) |
|
60 | expected_execution_count = expected_cell.get('execution_count', None) | |
60 | actual_execution_count = actual_cell.get('execution_count', None) |
|
61 | actual_execution_count = actual_cell.get('execution_count', None) | |
61 | self.assertEqual(expected_execution_count, actual_execution_count) |
|
62 | self.assertEqual(expected_execution_count, actual_execution_count) | |
62 |
|
63 | |||
63 |
|
64 | |||
64 | def build_preprocessor(self): |
|
65 | def build_preprocessor(self): | |
65 | """Make an instance of a preprocessor""" |
|
66 | """Make an instance of a preprocessor""" | |
66 | preprocessor = ExecutePreprocessor() |
|
67 | preprocessor = ExecutePreprocessor() | |
67 | preprocessor.enabled = True |
|
68 | preprocessor.enabled = True | |
68 | return preprocessor |
|
69 | return preprocessor | |
69 |
|
70 | |||
70 |
|
71 | |||
71 | def test_constructor(self): |
|
72 | def test_constructor(self): | |
72 | """Can a ExecutePreprocessor be constructed?""" |
|
73 | """Can a ExecutePreprocessor be constructed?""" | |
73 | self.build_preprocessor() |
|
74 | self.build_preprocessor() | |
74 |
|
75 | |||
75 |
|
76 | |||
76 | def test_run_notebooks(self): |
|
77 | def test_run_notebooks(self): | |
77 | """Runs a series of test notebooks and compares them to their actual output""" |
|
78 | """Runs a series of test notebooks and compares them to their actual output""" | |
78 | current_dir = os.path.dirname(__file__) |
|
79 | current_dir = os.path.dirname(__file__) | |
79 | input_files = glob.glob(os.path.join(current_dir, 'files', '*.ipynb')) |
|
80 | input_files = glob.glob(os.path.join(current_dir, 'files', '*.ipynb')) | |
80 | for filename in input_files: |
|
81 | for filename in input_files: | |
81 | with open(os.path.join(current_dir, 'files', filename)) as f: |
|
82 | with io.open(os.path.join(current_dir, 'files', filename)) as f: | |
82 |
input_nb = nbformat.read(f, |
|
83 | input_nb = nbformat.read(f, 4) | |
83 | res = self.build_resources() |
|
84 | res = self.build_resources() | |
84 | preprocessor = self.build_preprocessor() |
|
85 | preprocessor = self.build_preprocessor() | |
85 | cleaned_input_nb = copy.deepcopy(input_nb) |
|
86 | cleaned_input_nb = copy.deepcopy(input_nb) | |
86 | for cell in cleaned_input_nb.cells: |
|
87 | for cell in cleaned_input_nb.cells: | |
87 | if 'execution_count' in cell: |
|
88 | if 'execution_count' in cell: | |
88 | del cell['execution_count'] |
|
89 | del cell['execution_count'] | |
89 | cell['outputs'] = [] |
|
90 | cell['outputs'] = [] | |
90 | output_nb, _ = preprocessor(cleaned_input_nb, res) |
|
91 | output_nb, _ = preprocessor(cleaned_input_nb, res) | |
91 | self.assert_notebooks_equal(output_nb, input_nb) |
|
92 | self.assert_notebooks_equal(output_nb, input_nb) |
@@ -1,78 +1,78 | |||||
1 | """Tests for the revealhelp preprocessor""" |
|
1 | """Tests for the revealhelp preprocessor""" | |
2 |
|
2 | |||
3 | # Copyright (c) IPython Development Team. |
|
3 | # Copyright (c) IPython Development Team. | |
4 | # Distributed under the terms of the Modified BSD License. |
|
4 | # Distributed under the terms of the Modified BSD License. | |
5 |
|
5 | |||
6 |
from IPython.nbformat import |
|
6 | from IPython.nbformat import v4 as nbformat | |
7 |
|
7 | |||
8 | from .base import PreprocessorTestsBase |
|
8 | from .base import PreprocessorTestsBase | |
9 | from ..revealhelp import RevealHelpPreprocessor |
|
9 | from ..revealhelp import RevealHelpPreprocessor | |
10 |
|
10 | |||
11 |
|
11 | |||
12 | class Testrevealhelp(PreprocessorTestsBase): |
|
12 | class Testrevealhelp(PreprocessorTestsBase): | |
13 | """Contains test functions for revealhelp.py""" |
|
13 | """Contains test functions for revealhelp.py""" | |
14 |
|
14 | |||
15 | def build_notebook(self): |
|
15 | def build_notebook(self): | |
16 | """Build a reveal slides notebook in memory for use with tests. |
|
16 | """Build a reveal slides notebook in memory for use with tests. | |
17 | Overrides base in PreprocessorTestsBase""" |
|
17 | Overrides base in PreprocessorTestsBase""" | |
18 |
|
18 | |||
19 | outputs = [nbformat.new_output(output_type="stream", name="stdout", text="a")] |
|
19 | outputs = [nbformat.new_output(output_type="stream", name="stdout", text="a")] | |
20 |
|
20 | |||
21 | slide_metadata = {'slideshow' : {'slide_type': 'slide'}} |
|
21 | slide_metadata = {'slideshow' : {'slide_type': 'slide'}} | |
22 | subslide_metadata = {'slideshow' : {'slide_type': 'subslide'}} |
|
22 | subslide_metadata = {'slideshow' : {'slide_type': 'subslide'}} | |
23 |
|
23 | |||
24 | cells=[nbformat.new_code_cell(source="", execution_count=1, outputs=outputs), |
|
24 | cells=[nbformat.new_code_cell(source="", execution_count=1, outputs=outputs), | |
25 | nbformat.new_markdown_cell(source="", metadata=slide_metadata), |
|
25 | nbformat.new_markdown_cell(source="", metadata=slide_metadata), | |
26 | nbformat.new_code_cell(source="", execution_count=2, outputs=outputs), |
|
26 | nbformat.new_code_cell(source="", execution_count=2, outputs=outputs), | |
27 | nbformat.new_markdown_cell(source="", metadata=slide_metadata), |
|
27 | nbformat.new_markdown_cell(source="", metadata=slide_metadata), | |
28 | nbformat.new_markdown_cell(source="", metadata=subslide_metadata)] |
|
28 | nbformat.new_markdown_cell(source="", metadata=subslide_metadata)] | |
29 |
|
29 | |||
30 | return nbformat.new_notebook(cells=cells) |
|
30 | return nbformat.new_notebook(cells=cells) | |
31 |
|
31 | |||
32 |
|
32 | |||
33 | def build_preprocessor(self): |
|
33 | def build_preprocessor(self): | |
34 | """Make an instance of a preprocessor""" |
|
34 | """Make an instance of a preprocessor""" | |
35 | preprocessor = RevealHelpPreprocessor() |
|
35 | preprocessor = RevealHelpPreprocessor() | |
36 | preprocessor.enabled = True |
|
36 | preprocessor.enabled = True | |
37 | return preprocessor |
|
37 | return preprocessor | |
38 |
|
38 | |||
39 |
|
39 | |||
40 | def test_constructor(self): |
|
40 | def test_constructor(self): | |
41 | """Can a RevealHelpPreprocessor be constructed?""" |
|
41 | """Can a RevealHelpPreprocessor be constructed?""" | |
42 | self.build_preprocessor() |
|
42 | self.build_preprocessor() | |
43 |
|
43 | |||
44 |
|
44 | |||
45 | def test_reveal_attribute(self): |
|
45 | def test_reveal_attribute(self): | |
46 | """Make sure the reveal url_prefix resources is set""" |
|
46 | """Make sure the reveal url_prefix resources is set""" | |
47 | nb = self.build_notebook() |
|
47 | nb = self.build_notebook() | |
48 | res = self.build_resources() |
|
48 | res = self.build_resources() | |
49 | preprocessor = self.build_preprocessor() |
|
49 | preprocessor = self.build_preprocessor() | |
50 | nb, res = preprocessor(nb, res) |
|
50 | nb, res = preprocessor(nb, res) | |
51 | assert 'reveal' in res |
|
51 | assert 'reveal' in res | |
52 | assert 'url_prefix' in res['reveal'] |
|
52 | assert 'url_prefix' in res['reveal'] | |
53 |
|
53 | |||
54 |
|
54 | |||
55 | def test_reveal_output(self): |
|
55 | def test_reveal_output(self): | |
56 | """Make sure that the reveal preprocessor """ |
|
56 | """Make sure that the reveal preprocessor """ | |
57 | nb = self.build_notebook() |
|
57 | nb = self.build_notebook() | |
58 | res = self.build_resources() |
|
58 | res = self.build_resources() | |
59 | preprocessor = self.build_preprocessor() |
|
59 | preprocessor = self.build_preprocessor() | |
60 | nb, res = preprocessor(nb, res) |
|
60 | nb, res = preprocessor(nb, res) | |
61 | cells = nb.cells |
|
61 | cells = nb.cells | |
62 |
|
62 | |||
63 | # Make sure correct metadata tags are available on every cell. |
|
63 | # Make sure correct metadata tags are available on every cell. | |
64 | for cell in cells: |
|
64 | for cell in cells: | |
65 | assert 'slide_type' in cell.metadata |
|
65 | assert 'slide_type' in cell.metadata | |
66 |
|
66 | |||
67 | # Make sure slide end is only applied to the cells preceeding slide |
|
67 | # Make sure slide end is only applied to the cells preceeding slide | |
68 | # cells. |
|
68 | # cells. | |
69 | assert 'slide_helper' in cells[1].metadata |
|
69 | assert 'slide_helper' in cells[1].metadata | |
70 | self.assertEqual(cells[1].metadata['slide_helper'], '-') |
|
70 | self.assertEqual(cells[1].metadata['slide_helper'], '-') | |
71 |
|
71 | |||
72 | # Verify 'slide-end' |
|
72 | # Verify 'slide-end' | |
73 | assert 'slide_helper' in cells[0].metadata |
|
73 | assert 'slide_helper' in cells[0].metadata | |
74 | self.assertEqual(cells[0].metadata['slide_helper'], 'slide_end') |
|
74 | self.assertEqual(cells[0].metadata['slide_helper'], 'slide_end') | |
75 | assert 'slide_helper' in cells[2].metadata |
|
75 | assert 'slide_helper' in cells[2].metadata | |
76 | self.assertEqual(cells[2].metadata['slide_helper'], 'slide_end') |
|
76 | self.assertEqual(cells[2].metadata['slide_helper'], 'slide_end') | |
77 | assert 'slide_helper' in cells[3].metadata |
|
77 | assert 'slide_helper' in cells[3].metadata | |
78 | self.assertEqual(cells[3].metadata['slide_helper'], 'subslide_end') |
|
78 | self.assertEqual(cells[3].metadata['slide_helper'], 'subslide_end') |
@@ -1,74 +1,74 | |||||
1 | """Tests for the svg2pdf preprocessor""" |
|
1 | """Tests for the svg2pdf preprocessor""" | |
2 |
|
2 | |||
3 | # Copyright (c) IPython Development Team. |
|
3 | # Copyright (c) IPython Development Team. | |
4 | # Distributed under the terms of the Modified BSD License. |
|
4 | # Distributed under the terms of the Modified BSD License. | |
5 |
|
5 | |||
6 | from IPython.testing import decorators as dec |
|
6 | from IPython.testing import decorators as dec | |
7 |
from IPython.nbformat import |
|
7 | from IPython.nbformat import v4 as nbformat | |
8 |
|
8 | |||
9 | from .base import PreprocessorTestsBase |
|
9 | from .base import PreprocessorTestsBase | |
10 | from ..svg2pdf import SVG2PDFPreprocessor |
|
10 | from ..svg2pdf import SVG2PDFPreprocessor | |
11 |
|
11 | |||
12 |
|
12 | |||
13 | class Testsvg2pdf(PreprocessorTestsBase): |
|
13 | class Testsvg2pdf(PreprocessorTestsBase): | |
14 | """Contains test functions for svg2pdf.py""" |
|
14 | """Contains test functions for svg2pdf.py""" | |
15 |
|
15 | |||
16 | simple_svg = """<?xml version="1.0" encoding="UTF-8" standalone="no"?> |
|
16 | simple_svg = """<?xml version="1.0" encoding="UTF-8" standalone="no"?> | |
17 | <!-- Created with Inkscape (http://www.inkscape.org/) --> |
|
17 | <!-- Created with Inkscape (http://www.inkscape.org/) --> | |
18 | <svg |
|
18 | <svg | |
19 | xmlns:svg="http://www.w3.org/2000/svg" |
|
19 | xmlns:svg="http://www.w3.org/2000/svg" | |
20 | xmlns="http://www.w3.org/2000/svg" |
|
20 | xmlns="http://www.w3.org/2000/svg" | |
21 | version="1.0" |
|
21 | version="1.0" | |
22 | x="0.00000000" |
|
22 | x="0.00000000" | |
23 | y="0.00000000" |
|
23 | y="0.00000000" | |
24 | width="500.00000" |
|
24 | width="500.00000" | |
25 | height="500.00000" |
|
25 | height="500.00000" | |
26 | id="svg2"> |
|
26 | id="svg2"> | |
27 | <defs |
|
27 | <defs | |
28 | id="defs4" /> |
|
28 | id="defs4" /> | |
29 | <g |
|
29 | <g | |
30 | id="layer1"> |
|
30 | id="layer1"> | |
31 | <rect |
|
31 | <rect | |
32 | width="300.00000" |
|
32 | width="300.00000" | |
33 | height="300.00000" |
|
33 | height="300.00000" | |
34 | x="100.00000" |
|
34 | x="100.00000" | |
35 | y="100.00000" |
|
35 | y="100.00000" | |
36 | style="opacity:1.0000000;fill:none;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:8.0000000;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-dashoffset:0.00000000;stroke-opacity:1.0000000" |
|
36 | style="opacity:1.0000000;fill:none;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:8.0000000;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-dashoffset:0.00000000;stroke-opacity:1.0000000" | |
37 | id="rect5719" /> |
|
37 | id="rect5719" /> | |
38 | </g> |
|
38 | </g> | |
39 | </svg>""" |
|
39 | </svg>""" | |
40 |
|
40 | |||
41 | def build_notebook(self): |
|
41 | def build_notebook(self): | |
42 | """Build a reveal slides notebook in memory for use with tests. |
|
42 | """Build a reveal slides notebook in memory for use with tests. | |
43 | Overrides base in PreprocessorTestsBase""" |
|
43 | Overrides base in PreprocessorTestsBase""" | |
44 |
|
44 | |||
45 | outputs = [nbformat.new_output(output_type="image/svg+xml", output_svg=self.simple_svg)] |
|
45 | outputs = [nbformat.new_output(output_type="image/svg+xml", output_svg=self.simple_svg)] | |
46 |
|
46 | |||
47 | slide_metadata = {'slideshow' : {'slide_type': 'slide'}} |
|
47 | slide_metadata = {'slideshow' : {'slide_type': 'slide'}} | |
48 | subslide_metadata = {'slideshow' : {'slide_type': 'subslide'}} |
|
48 | subslide_metadata = {'slideshow' : {'slide_type': 'subslide'}} | |
49 |
|
49 | |||
50 | cells=[nbformat.new_code_cell(source="", execution_count=1, outputs=outputs)] |
|
50 | cells=[nbformat.new_code_cell(source="", execution_count=1, outputs=outputs)] | |
51 |
|
51 | |||
52 | return nbformat.new_notebook(cells=cells) |
|
52 | return nbformat.new_notebook(cells=cells) | |
53 |
|
53 | |||
54 |
|
54 | |||
55 | def build_preprocessor(self): |
|
55 | def build_preprocessor(self): | |
56 | """Make an instance of a preprocessor""" |
|
56 | """Make an instance of a preprocessor""" | |
57 | preprocessor = SVG2PDFPreprocessor() |
|
57 | preprocessor = SVG2PDFPreprocessor() | |
58 | preprocessor.enabled = True |
|
58 | preprocessor.enabled = True | |
59 | return preprocessor |
|
59 | return preprocessor | |
60 |
|
60 | |||
61 |
|
61 | |||
62 | def test_constructor(self): |
|
62 | def test_constructor(self): | |
63 | """Can a SVG2PDFPreprocessor be constructed?""" |
|
63 | """Can a SVG2PDFPreprocessor be constructed?""" | |
64 | self.build_preprocessor() |
|
64 | self.build_preprocessor() | |
65 |
|
65 | |||
66 |
|
66 | |||
67 | @dec.onlyif_cmds_exist('inkscape') |
|
67 | @dec.onlyif_cmds_exist('inkscape') | |
68 | def test_output(self): |
|
68 | def test_output(self): | |
69 | """Test the output of the SVG2PDFPreprocessor""" |
|
69 | """Test the output of the SVG2PDFPreprocessor""" | |
70 | nb = self.build_notebook() |
|
70 | nb = self.build_notebook() | |
71 | res = self.build_resources() |
|
71 | res = self.build_resources() | |
72 | preprocessor = self.build_preprocessor() |
|
72 | preprocessor = self.build_preprocessor() | |
73 | nb, res = preprocessor(nb, res) |
|
73 | nb, res = preprocessor(nb, res) | |
74 | self.assertIn('application/pdf', nb.cells[0].outputs[0]) |
|
74 | self.assertIn('application/pdf', nb.cells[0].outputs[0]) |
@@ -1,149 +1,148 | |||||
1 | """Base test class for nbconvert""" |
|
1 | """Base test class for nbconvert""" | |
2 |
|
2 | |||
3 | # Copyright (c) IPython Development Team. |
|
3 | # Copyright (c) IPython Development Team. | |
4 | # Distributed under the terms of the Modified BSD License. |
|
4 | # Distributed under the terms of the Modified BSD License. | |
5 |
|
5 | |||
6 | import io |
|
6 | import io | |
7 | import os |
|
7 | import os | |
8 | import glob |
|
8 | import glob | |
9 | import shutil |
|
9 | import shutil | |
10 | import unittest |
|
10 | import unittest | |
11 |
|
11 | |||
12 | import IPython |
|
12 | import IPython | |
13 |
from IPython.nbformat import |
|
13 | from IPython.nbformat import v4, write | |
14 | from IPython.utils.tempdir import TemporaryWorkingDirectory |
|
14 | from IPython.utils.tempdir import TemporaryWorkingDirectory | |
15 | from IPython.utils.path import get_ipython_package_dir |
|
15 | from IPython.utils.path import get_ipython_package_dir | |
16 | from IPython.utils.process import get_output_error_code |
|
16 | from IPython.utils.process import get_output_error_code | |
17 | from IPython.testing.tools import get_ipython_cmd |
|
17 | from IPython.testing.tools import get_ipython_cmd | |
18 |
|
18 | |||
19 | # a trailing space allows for simpler concatenation with the other arguments |
|
19 | # a trailing space allows for simpler concatenation with the other arguments | |
20 | ipy_cmd = get_ipython_cmd(as_string=True) + " " |
|
20 | ipy_cmd = get_ipython_cmd(as_string=True) + " " | |
21 |
|
21 | |||
22 |
|
22 | |||
23 | class TestsBase(unittest.TestCase): |
|
23 | class TestsBase(unittest.TestCase): | |
24 | """Base tests class. Contains useful fuzzy comparison and nbconvert |
|
24 | """Base tests class. Contains useful fuzzy comparison and nbconvert | |
25 | functions.""" |
|
25 | functions.""" | |
26 |
|
26 | |||
27 |
|
27 | |||
28 | def fuzzy_compare(self, a, b, newlines_are_spaces=True, tabs_are_spaces=True, |
|
28 | def fuzzy_compare(self, a, b, newlines_are_spaces=True, tabs_are_spaces=True, | |
29 | fuzzy_spacing=True, ignore_spaces=False, |
|
29 | fuzzy_spacing=True, ignore_spaces=False, | |
30 | ignore_newlines=False, case_sensitive=False, leave_padding=False): |
|
30 | ignore_newlines=False, case_sensitive=False, leave_padding=False): | |
31 | """ |
|
31 | """ | |
32 | Performs a fuzzy comparison of two strings. A fuzzy comparison is a |
|
32 | Performs a fuzzy comparison of two strings. A fuzzy comparison is a | |
33 | comparison that ignores insignificant differences in the two comparands. |
|
33 | comparison that ignores insignificant differences in the two comparands. | |
34 | The significance of certain differences can be specified via the keyword |
|
34 | The significance of certain differences can be specified via the keyword | |
35 | parameters of this method. |
|
35 | parameters of this method. | |
36 | """ |
|
36 | """ | |
37 |
|
37 | |||
38 | if not leave_padding: |
|
38 | if not leave_padding: | |
39 | a = a.strip() |
|
39 | a = a.strip() | |
40 | b = b.strip() |
|
40 | b = b.strip() | |
41 |
|
41 | |||
42 | if ignore_newlines: |
|
42 | if ignore_newlines: | |
43 | a = a.replace('\n', '') |
|
43 | a = a.replace('\n', '') | |
44 | b = b.replace('\n', '') |
|
44 | b = b.replace('\n', '') | |
45 |
|
45 | |||
46 | if newlines_are_spaces: |
|
46 | if newlines_are_spaces: | |
47 | a = a.replace('\n', ' ') |
|
47 | a = a.replace('\n', ' ') | |
48 | b = b.replace('\n', ' ') |
|
48 | b = b.replace('\n', ' ') | |
49 |
|
49 | |||
50 | if tabs_are_spaces: |
|
50 | if tabs_are_spaces: | |
51 | a = a.replace('\t', ' ') |
|
51 | a = a.replace('\t', ' ') | |
52 | b = b.replace('\t', ' ') |
|
52 | b = b.replace('\t', ' ') | |
53 |
|
53 | |||
54 | if ignore_spaces: |
|
54 | if ignore_spaces: | |
55 | a = a.replace(' ', '') |
|
55 | a = a.replace(' ', '') | |
56 | b = b.replace(' ', '') |
|
56 | b = b.replace(' ', '') | |
57 |
|
57 | |||
58 | if fuzzy_spacing: |
|
58 | if fuzzy_spacing: | |
59 | a = self.recursive_replace(a, ' ', ' ') |
|
59 | a = self.recursive_replace(a, ' ', ' ') | |
60 | b = self.recursive_replace(b, ' ', ' ') |
|
60 | b = self.recursive_replace(b, ' ', ' ') | |
61 |
|
61 | |||
62 | if not case_sensitive: |
|
62 | if not case_sensitive: | |
63 | a = a.lower() |
|
63 | a = a.lower() | |
64 | b = b.lower() |
|
64 | b = b.lower() | |
65 |
|
65 | |||
66 | self.assertEqual(a, b) |
|
66 | self.assertEqual(a, b) | |
67 |
|
67 | |||
68 |
|
68 | |||
69 | def recursive_replace(self, text, search, replacement): |
|
69 | def recursive_replace(self, text, search, replacement): | |
70 | """ |
|
70 | """ | |
71 | Performs a recursive replacement operation. Replaces all instances |
|
71 | Performs a recursive replacement operation. Replaces all instances | |
72 | of a search string in a text string with a replacement string until |
|
72 | of a search string in a text string with a replacement string until | |
73 | the search string no longer exists. Recursion is needed because the |
|
73 | the search string no longer exists. Recursion is needed because the | |
74 | replacement string may generate additional search strings. |
|
74 | replacement string may generate additional search strings. | |
75 |
|
75 | |||
76 | For example: |
|
76 | For example: | |
77 | Replace "ii" with "i" in the string "Hiiii" yields "Hii" |
|
77 | Replace "ii" with "i" in the string "Hiiii" yields "Hii" | |
78 | Another replacement cds "Hi" (the desired output) |
|
78 | Another replacement cds "Hi" (the desired output) | |
79 |
|
79 | |||
80 | Parameters |
|
80 | Parameters | |
81 | ---------- |
|
81 | ---------- | |
82 | text : string |
|
82 | text : string | |
83 | Text to replace in. |
|
83 | Text to replace in. | |
84 | search : string |
|
84 | search : string | |
85 | String to search for within "text" |
|
85 | String to search for within "text" | |
86 | replacement : string |
|
86 | replacement : string | |
87 | String to replace "search" with |
|
87 | String to replace "search" with | |
88 | """ |
|
88 | """ | |
89 | while search in text: |
|
89 | while search in text: | |
90 | text = text.replace(search, replacement) |
|
90 | text = text.replace(search, replacement) | |
91 | return text |
|
91 | return text | |
92 |
|
92 | |||
93 | def create_temp_cwd(self, copy_filenames=None): |
|
93 | def create_temp_cwd(self, copy_filenames=None): | |
94 | temp_dir = TemporaryWorkingDirectory() |
|
94 | temp_dir = TemporaryWorkingDirectory() | |
95 |
|
95 | |||
96 | #Copy the files if requested. |
|
96 | #Copy the files if requested. | |
97 | if copy_filenames is not None: |
|
97 | if copy_filenames is not None: | |
98 | self.copy_files_to(copy_filenames, dest=temp_dir.name) |
|
98 | self.copy_files_to(copy_filenames, dest=temp_dir.name) | |
99 |
|
99 | |||
100 | #Return directory handler |
|
100 | #Return directory handler | |
101 | return temp_dir |
|
101 | return temp_dir | |
102 |
|
102 | |||
103 | def create_empty_notebook(self, path): |
|
103 | def create_empty_notebook(self, path): | |
104 |
nb = |
|
104 | nb = v4.new_notebook() | |
105 | with io.open(path, 'w', encoding='utf-8') as f: |
|
105 | with io.open(path, 'w', encoding='utf-8') as f: | |
106 |
|
|
106 | write(f, nb, 4) | |
107 |
|
||||
108 |
|
107 | |||
109 | def copy_files_to(self, copy_filenames, dest='.'): |
|
108 | def copy_files_to(self, copy_filenames, dest='.'): | |
110 | "Copy test files into the destination directory" |
|
109 | "Copy test files into the destination directory" | |
111 | if not os.path.isdir(dest): |
|
110 | if not os.path.isdir(dest): | |
112 | os.makedirs(dest) |
|
111 | os.makedirs(dest) | |
113 | files_path = self._get_files_path() |
|
112 | files_path = self._get_files_path() | |
114 | for pattern in copy_filenames: |
|
113 | for pattern in copy_filenames: | |
115 | for match in glob.glob(os.path.join(files_path, pattern)): |
|
114 | for match in glob.glob(os.path.join(files_path, pattern)): | |
116 | shutil.copyfile(match, os.path.join(dest, os.path.basename(match))) |
|
115 | shutil.copyfile(match, os.path.join(dest, os.path.basename(match))) | |
117 |
|
116 | |||
118 |
|
117 | |||
119 | def _get_files_path(self): |
|
118 | def _get_files_path(self): | |
120 |
|
119 | |||
121 | #Get the relative path to this module in the IPython directory. |
|
120 | #Get the relative path to this module in the IPython directory. | |
122 | names = self.__module__.split('.')[1:-1] |
|
121 | names = self.__module__.split('.')[1:-1] | |
123 | names.append('files') |
|
122 | names.append('files') | |
124 |
|
123 | |||
125 | #Build a path using the IPython directory and the relative path we just |
|
124 | #Build a path using the IPython directory and the relative path we just | |
126 | #found. |
|
125 | #found. | |
127 | path = get_ipython_package_dir() |
|
126 | path = get_ipython_package_dir() | |
128 | for name in names: |
|
127 | for name in names: | |
129 | path = os.path.join(path, name) |
|
128 | path = os.path.join(path, name) | |
130 | return path |
|
129 | return path | |
131 |
|
130 | |||
132 |
|
131 | |||
133 | def call(self, parameters, ignore_return_code=False): |
|
132 | def call(self, parameters, ignore_return_code=False): | |
134 | """ |
|
133 | """ | |
135 | Execute a, IPython shell command, listening for both Errors and non-zero |
|
134 | Execute a, IPython shell command, listening for both Errors and non-zero | |
136 | return codes. |
|
135 | return codes. | |
137 |
|
136 | |||
138 | Parameters |
|
137 | Parameters | |
139 | ---------- |
|
138 | ---------- | |
140 | parameters : str |
|
139 | parameters : str | |
141 | List of parameters to pass to IPython. |
|
140 | List of parameters to pass to IPython. | |
142 | ignore_return_code : optional bool (default False) |
|
141 | ignore_return_code : optional bool (default False) | |
143 | Throw an OSError if the return code |
|
142 | Throw an OSError if the return code | |
144 | """ |
|
143 | """ | |
145 |
|
144 | |||
146 | stdout, stderr, retcode = get_output_error_code(ipy_cmd + parameters) |
|
145 | stdout, stderr, retcode = get_output_error_code(ipy_cmd + parameters) | |
147 | if not (retcode == 0 or ignore_return_code): |
|
146 | if not (retcode == 0 or ignore_return_code): | |
148 | raise OSError(stderr) |
|
147 | raise OSError(stderr) | |
149 | return stdout, stderr |
|
148 | return stdout, stderr |
General Comments 0
You need to be logged in to leave comments.
Login now