##// END OF EJS Templates
Rebase changes made by hand
Jonathan Frederic -
Show More
@@ -1,9 +1,9 b''
1 1 from .export import *
2 2 from .html import HTMLExporter
3 3 from .slides import SlidesExporter
4 from .exporter import TemplateExporter
4 from .templateexporter import TemplateExporter
5 5 from .latex import LatexExporter
6 6 from .markdown import MarkdownExporter
7 7 from .python import PythonExporter
8 8 from .rst import RSTExporter
9 from .baseexporter import BaseExporter
9 from .exporter import Exporter
@@ -18,7 +18,7 b' from functools import wraps'
18 18 from IPython.nbformat.v3.nbbase import NotebookNode
19 19 from IPython.config import Config
20 20
21 from .exporter import TemplateExporter
21 from .templateexporter import TemplateExporter
22 22 from .html import HTMLExporter
23 23 from .slides import SlidesExporter
24 24 from .latex import LatexExporter
@@ -1,4 +1,4 b''
1 """This module defines BaseExporter, a highly configurable converter
1 """This module defines Exporter, a highly configurable converter
2 2 that uses Jinja2 to export notebook files into different formats.
3 3 """
4 4
@@ -30,9 +30,9 b' from IPython.config import Config'
30 30 from IPython.nbformat import current as nbformat
31 31 from IPython.utils.traitlets import MetaHasTraits, Unicode, List
32 32 from IPython.utils.importstring import import_item
33 from IPython.utils import py3compat
33 from IPython.utils import text, py3compat
34 34
35 from IPython.nbconvert import transformers as nbtransformers
35 from IPython.nbconvert import preprocessors as nbpreprocessors
36 36
37 37
38 38 #-----------------------------------------------------------------------------
@@ -44,12 +44,11 b' class ResourcesDict(collections.defaultdict):'
44 44 return ''
45 45
46 46
47 class BaseExporter(LoggingConfigurable):
47 class Exporter(LoggingConfigurable):
48 48 """
49 Base Exporter Class that only conver notebook to notebook
50 and apply the transformers and provide basic methods for
49 Exporter class that only converts from notebook to notebook
50 by applying the preprocessors and providing basic methods for
51 51 reading a notebook from different sources.
52
53 52 """
54 53
55 54 # finish the docstring
@@ -59,21 +58,21 b' class BaseExporter(LoggingConfigurable):'
59 58 help="Extension of the file that should be written to disk"
60 59 )
61 60
62 #Configurability, allows the user to easily add transformers.
63 transformers = List(config=True,
64 help="""List of transformers, by name or namespace, to enable.""")
61 #Configurability, allows the user to easily add filters and preprocessors.
62 preprocessors = List(config=True,
63 help="""List of preprocessors, by name or namespace, to enable.""")
65 64
66 _transformers = None
65 _preprocessors = None
67 66
68 default_transformers = List([nbtransformers.coalesce_streams,
69 nbtransformers.SVG2PDFTransformer,
70 nbtransformers.ExtractOutputTransformer,
71 nbtransformers.CSSHTMLHeaderTransformer,
72 nbtransformers.RevealHelpTransformer,
73 nbtransformers.LatexTransformer,
74 nbtransformers.SphinxTransformer],
67 default_preprocessors = List([nbpreprocessors.coalesce_streams,
68 nbpreprocessors.SVG2PDFPreprocessor,
69 nbpreprocessors.ExtractOutputPreprocessor,
70 nbpreprocessors.CSSHTMLHeaderPreprocessor,
71 nbpreprocessors.RevealHelpPreprocessor,
72 nbpreprocessors.LatexPreprocessor,
73 nbpreprocessors.SphinxPreprocessor],
75 74 config=True,
76 help="""List of transformers available by default, by name, namespace,
75 help="""List of preprocessors available by default, by name, namespace,
77 76 instance, or type.""")
78 77
79 78
@@ -89,10 +88,10 b' class BaseExporter(LoggingConfigurable):'
89 88 if not config:
90 89 config = self.default_config
91 90
92 super(BaseExporter, self).__init__(config=config, **kw)
91 super(Exporter, self).__init__(config=config, **kw)
93 92
94 93 #Init
95 self._init_transformers()
94 self._init_preprocessors()
96 95
97 96
98 97 @property
@@ -106,7 +105,7 b' class BaseExporter(LoggingConfigurable):'
106 105 c.merge(new)
107 106 if c != old:
108 107 self.config = c
109 super(BaseExporter, self)._config_changed(name, old, c)
108 super(Exporter, self)._config_changed(name, old, c)
110 109
111 110
112 111 def from_notebook_node(self, nb, resources=None):
@@ -118,7 +117,7 b' class BaseExporter(LoggingConfigurable):'
118 117 nb : Notebook node
119 118 resources : dict (**kw)
120 119 of additional resources that can be accessed read/write by
121 transformers.
120 preprocessors.
122 121 """
123 122 nb_copy = copy.deepcopy(nb)
124 123 resources = self._init_resources(resources)
@@ -139,7 +138,7 b' class BaseExporter(LoggingConfigurable):'
139 138 Full filename of the notebook file to open and convert.
140 139 """
141 140
142 #Pull the metadata from the filesystem.
141 # Pull the metadata from the filesystem.
143 142 if resources is None:
144 143 resources = ResourcesDict()
145 144 if not 'metadata' in resources or resources['metadata'] == '':
@@ -149,7 +148,7 b' class BaseExporter(LoggingConfigurable):'
149 148 resources['metadata']['name'] = notebook_name
150 149
151 150 modified_date = datetime.datetime.fromtimestamp(os.path.getmtime(filename))
152 resources['metadata']['modified_date'] = modified_date.strftime("%B %d, %Y")
151 resources['metadata']['modified_date'] = modified_date.strftime(text.date_format)
153 152
154 153 with io.open(filename) as f:
155 154 return self.from_notebook_node(nbformat.read(f, 'json'), resources=resources, **kw)
@@ -167,70 +166,70 b' class BaseExporter(LoggingConfigurable):'
167 166 return self.from_notebook_node(nbformat.read(file_stream, 'json'), resources=resources, **kw)
168 167
169 168
170 def register_transformer(self, transformer, enabled=False):
169 def register_preprocessor(self, preprocessor, enabled=False):
171 170 """
172 Register a transformer.
173 Transformers are classes that act upon the notebook before it is
174 passed into the Jinja templating engine. Transformers are also
171 Register a preprocessor.
172 preprocessors are classes that act upon the notebook before it is
173 passed into the Jinja templating engine. preprocessors are also
175 174 capable of passing additional information to the Jinja
176 175 templating engine.
177 176
178 177 Parameters
179 178 ----------
180 transformer : transformer
179 preprocessor : preprocessor
181 180 """
182 if transformer is None:
183 raise TypeError('transformer')
184 isclass = isinstance(transformer, type)
181 if preprocessor is None:
182 raise TypeError('preprocessor')
183 isclass = isinstance(preprocessor, type)
185 184 constructed = not isclass
186 185
187 #Handle transformer's registration based on it's type
188 if constructed and isinstance(transformer, py3compat.string_types):
189 #Transformer is a string, import the namespace and recursively call
190 #this register_transformer method
191 transformer_cls = import_item(transformer)
192 return self.register_transformer(transformer_cls, enabled)
186 #Handle preprocessor's registration based on it's type
187 if constructed and isinstance(preprocessor, py3compat.string_types):
188 #preprocessor is a string, import the namespace and recursively call
189 #this register_preprocessor method
190 preprocessor_cls = import_item(preprocessor)
191 return self.register_preprocessor(preprocessor_cls, enabled)
193 192
194 if constructed and hasattr(transformer, '__call__'):
195 #Transformer is a function, no need to construct it.
196 #Register and return the transformer.
193 if constructed and hasattr(preprocessor, '__call__'):
194 #preprocessor is a function, no need to construct it.
195 #Register and return the preprocessor.
197 196 if enabled:
198 transformer.enabled = True
199 self._transformers.append(transformer)
200 return transformer
197 preprocessor.enabled = True
198 self._preprocessors.append(preprocessor)
199 return preprocessor
201 200
202 elif isclass and isinstance(transformer, MetaHasTraits):
203 #Transformer is configurable. Make sure to pass in new default for
201 elif isclass and isinstance(preprocessor, MetaHasTraits):
202 #preprocessor is configurable. Make sure to pass in new default for
204 203 #the enabled flag if one was specified.
205 self.register_transformer(transformer(parent=self), enabled)
204 self.register_preprocessor(preprocessor(parent=self), enabled)
206 205
207 206 elif isclass:
208 #Transformer is not configurable, construct it
209 self.register_transformer(transformer(), enabled)
207 #preprocessor is not configurable, construct it
208 self.register_preprocessor(preprocessor(), enabled)
210 209
211 210 else:
212 #Transformer is an instance of something without a __call__
211 #preprocessor is an instance of something without a __call__
213 212 #attribute.
214 raise TypeError('transformer')
213 raise TypeError('preprocessor')
215 214
216 215
217 def _init_transformers(self):
216 def _init_preprocessors(self):
218 217 """
219 Register all of the transformers needed for this exporter, disabled
218 Register all of the preprocessors needed for this exporter, disabled
220 219 unless specified explicitly.
221 220 """
222 if self._transformers is None:
223 self._transformers = []
221 if self._preprocessors is None:
222 self._preprocessors = []
224 223
225 #Load default transformers (not necessarly enabled by default).
226 if self.default_transformers:
227 for transformer in self.default_transformers:
228 self.register_transformer(transformer)
224 #Load default preprocessors (not necessarly enabled by default).
225 if self.default_preprocessors:
226 for preprocessor in self.default_preprocessors:
227 self.register_preprocessor(preprocessor)
229 228
230 #Load user transformers. Enable by default.
231 if self.transformers:
232 for transformer in self.transformers:
233 self.register_transformer(transformer, enabled=True)
229 #Load user preprocessors. Enable by default.
230 if self.preprocessors:
231 for preprocessor in self.preprocessors:
232 self.register_preprocessor(preprocessor, enabled=True)
234 233
235 234
236 235 def _init_resources(self, resources):
@@ -267,16 +266,16 b' class BaseExporter(LoggingConfigurable):'
267 266 nb : notebook node
268 267 notebook that is being exported.
269 268 resources : a dict of additional resources that
270 can be accessed read/write by transformers
269 can be accessed read/write by preprocessors
271 270 """
272 271
273 272 # Do a copy.deepcopy first,
274 # we are never safe enough with what the transformers could do.
273 # we are never safe enough with what the preprocessors could do.
275 274 nbc = copy.deepcopy(nb)
276 275 resc = copy.deepcopy(resources)
277 276
278 #Run each transformer on the notebook. Carry the output along
279 #to each transformer
280 for transformer in self._transformers:
281 nbc, resc = transformer(nbc, resc)
277 #Run each preprocessor on the notebook. Carry the output along
278 #to each preprocessor
279 for preprocessor in self._preprocessors:
280 nbc, resc = preprocessor(nbc, resc)
282 281 return nbc, resc
@@ -19,7 +19,7 b' from IPython.utils.traitlets import Unicode, List'
19 19 from IPython.nbconvert import preprocessors
20 20 from IPython.config import Config
21 21
22 from .exporter import TemplateExporter
22 from .templateexporter import TemplateExporter
23 23
24 24 #-----------------------------------------------------------------------------
25 25 # Classes
@@ -24,13 +24,13 b' from IPython.utils.traitlets import Unicode, List'
24 24 from IPython.config import Config
25 25
26 26 from IPython.nbconvert import filters, preprocessors
27 from .exporter import Exporter
27 from .templateexporter import TemplateExporter
28 28
29 29 #-----------------------------------------------------------------------------
30 30 # Classes and functions
31 31 #-----------------------------------------------------------------------------
32 32
33 class LatexExporter(Exporter):
33 class LatexExporter(TemplateExporter):
34 34 """
35 35 Exports to a Latex template. Inherit from this class if your template is
36 36 LaTeX based and you need custom tranformers/filters. Inherit from it if
@@ -16,7 +16,7 b' Exporter that will export your ipynb to Markdown.'
16 16 from IPython.config import Config
17 17 from IPython.utils.traitlets import Unicode
18 18
19 from .exporter import TemplateExporter
19 from .templateexporter import TemplateExporter
20 20
21 21 #-----------------------------------------------------------------------------
22 22 # Classes
@@ -15,7 +15,7 b' Python exporter which exports Notebook code into a PY file.'
15 15
16 16 from IPython.utils.traitlets import Unicode
17 17
18 from .exporter import TemplateExporter
18 from .templateexporter import TemplateExporter
19 19
20 20 #-----------------------------------------------------------------------------
21 21 # Classes
@@ -16,7 +16,7 b' Exporter for exporting notebooks to restructured text.'
16 16 from IPython.utils.traitlets import Unicode
17 17 from IPython.config import Config
18 18
19 from .exporter import TemplateExporter
19 from .templateexporter import TemplateExporter
20 20
21 21 #-----------------------------------------------------------------------------
22 22 # Classes
@@ -19,7 +19,7 b' from IPython.utils.traitlets import Unicode'
19 19 from IPython.nbconvert import preprocessors
20 20 from IPython.config import Config
21 21
22 from .exporter import TemplateExporter
22 from .templateexporter import TemplateExporter
23 23
24 24 #-----------------------------------------------------------------------------
25 25 # Classes
@@ -17,27 +17,18 b' that uses Jinja2 to export notebook files into different formats.'
17 17 from __future__ import print_function, absolute_import
18 18
19 19 # Stdlib imports
20 import io
21 20 import os
22 import inspect
23 import copy
24 import collections
25 import datetime
26 21
27 22 # other libs/dependencies
28 23 from jinja2 import Environment, FileSystemLoader, ChoiceLoader, TemplateNotFound
29 24
30 25 # IPython imports
31 from IPython.config.configurable import LoggingConfigurable
32 from IPython.config import Config
33 from IPython.nbformat import current as nbformat
34 from IPython.utils.traitlets import MetaHasTraits, DottedObjectName, Unicode, List, Dict, Any
26 from IPython.utils.traitlets import MetaHasTraits, Unicode, List, Dict, Any
35 27 from IPython.utils.importstring import import_item
36 from IPython.utils import text
37 from IPython.utils import py3compat
28 from IPython.utils import py3compat, text
38 29
39 from IPython.nbconvert import preprocessors as nbpreprocessors
40 30 from IPython.nbconvert import filters
31 from .exporter import Exporter
41 32
42 33 #-----------------------------------------------------------------------------
43 34 # Globals and constants
@@ -76,12 +67,7 b' default_filters = {'
76 67 # Class
77 68 #-----------------------------------------------------------------------------
78 69
79 class ResourcesDict(collections.defaultdict):
80 def __missing__(self, key):
81 return ''
82
83
84 class Exporter(LoggingConfigurable):
70 class TemplateExporter(Exporter):
85 71 """
86 72 Exports notebooks into other file formats. Uses Jinja 2 templating engine
87 73 to output new formats. Inherit from this class if you are creating a new
@@ -101,7 +87,7 b' class Exporter(LoggingConfigurable):'
101 87 config=True,
102 88 help="Name of the template file to use")
103 89 def _template_file_changed(self, name, old, new):
104 if new=='default':
90 if new == 'default':
105 91 self.template_file = self.default_template
106 92 else:
107 93 self.template_file = new
@@ -112,11 +98,6 b' class Exporter(LoggingConfigurable):'
112 98 template = Any()
113 99 environment = Any()
114 100
115 file_extension = Unicode(
116 'txt', config=True,
117 help="Extension of the file that should be written to disk"
118 )
119
120 101 template_path = List(['.'], config=True)
121 102 def _template_path_changed(self, name, old, new):
122 103 self._load_template()
@@ -140,25 +121,10 b' class Exporter(LoggingConfigurable):'
140 121 #Extension that the template files use.
141 122 template_extension = Unicode(".tpl", config=True)
142 123
143 #Configurability, allows the user to easily add filters and preprocessors.
144 preprocessors = List(config=True,
145 help="""List of preprocessors, by name or namespace, to enable.""")
146
147 124 filters = Dict(config=True,
148 125 help="""Dictionary of filters, by name and namespace, to add to the Jinja
149 126 environment.""")
150 127
151 default_preprocessors = List([nbpreprocessors.coalesce_streams,
152 nbpreprocessors.SVG2PDFPreprocessor,
153 nbpreprocessors.ExtractOutputPreprocessor,
154 nbpreprocessors.CSSHTMLHeaderPreprocessor,
155 nbpreprocessors.RevealHelpPreprocessor,
156 nbpreprocessors.LatexPreprocessor,
157 nbpreprocessors.SphinxPreprocessor],
158 config=True,
159 help="""List of preprocessors available by default, by name, namespace,
160 instance, or type.""")
161
162 128
163 129 def __init__(self, config=None, extra_loaders=None, **kw):
164 130 """
@@ -186,20 +152,6 b' class Exporter(LoggingConfigurable):'
186 152 self._init_filters()
187 153
188 154
189 @property
190 def default_config(self):
191 return Config()
192
193 def _config_changed(self, name, old, new):
194 """When setting config, make sure to start with our default_config"""
195 c = self.default_config
196 if new:
197 c.merge(new)
198 if c != old:
199 self.config = c
200 super(Exporter, self)._config_changed(name, old, c)
201
202
203 155 def _load_template(self):
204 156 """Load the Jinja template object from the template file
205 157
@@ -249,11 +201,7 b' class Exporter(LoggingConfigurable):'
249 201 of additional resources that can be accessed read/write by
250 202 preprocessors and filters.
251 203 """
252 nb_copy = copy.deepcopy(nb)
253 resources = self._init_resources(resources)
254
255 # Preprocess
256 nb_copy, resources = self._preprocess(nb_copy, resources)
204 nb_copy, resources = super(TemplateExporter, self).from_notebook_node(nb, resources, **kw)
257 205
258 206 self._load_template()
259 207
@@ -264,91 +212,6 b' class Exporter(LoggingConfigurable):'
264 212 return output, resources
265 213
266 214
267 def from_filename(self, filename, resources=None, **kw):
268 """
269 Convert a notebook from a notebook file.
270
271 Parameters
272 ----------
273 filename : str
274 Full filename of the notebook file to open and convert.
275 """
276
277 #Pull the metadata from the filesystem.
278 if resources is None:
279 resources = ResourcesDict()
280 if not 'metadata' in resources or resources['metadata'] == '':
281 resources['metadata'] = ResourcesDict()
282 basename = os.path.basename(filename)
283 notebook_name = basename[:basename.rfind('.')]
284 resources['metadata']['name'] = notebook_name
285
286 modified_date = datetime.datetime.fromtimestamp(os.path.getmtime(filename))
287 resources['metadata']['modified_date'] = modified_date.strftime(text.date_format)
288
289 with io.open(filename) as f:
290 return self.from_notebook_node(nbformat.read(f, 'json'), resources=resources,**kw)
291
292
293 def from_file(self, file_stream, resources=None, **kw):
294 """
295 Convert a notebook from a notebook file.
296
297 Parameters
298 ----------
299 file_stream : file-like object
300 Notebook file-like object to convert.
301 """
302 return self.from_notebook_node(nbformat.read(file_stream, 'json'), resources=resources, **kw)
303
304
305 def register_preprocessor(self, preprocessor, enabled=False):
306 """
307 Register a preprocessor.
308 Preprocessors are classes that act upon the notebook before it is
309 passed into the Jinja templating engine. Preprocessors are also
310 capable of passing additional information to the Jinja
311 templating engine.
312
313 Parameters
314 ----------
315 preprocessor : preprocessor
316 """
317 if preprocessor is None:
318 raise TypeError('preprocessor')
319 isclass = isinstance(preprocessor, type)
320 constructed = not isclass
321
322 #Handle preprocessor's registration based on it's type
323 if constructed and isinstance(preprocessor, py3compat.string_types):
324 #Preprocessor is a string, import the namespace and recursively call
325 #this register_preprocessor method
326 preprocessor_cls = import_item(preprocessor)
327 return self.register_preprocessor(preprocessor_cls, enabled)
328
329 if constructed and hasattr(preprocessor, '__call__'):
330 #Preprocessor is a function, no need to construct it.
331 #Register and return the preprocessor.
332 if enabled:
333 preprocessor.enabled = True
334 self._preprocessors.append(preprocessor)
335 return preprocessor
336
337 elif isclass and isinstance(preprocessor, MetaHasTraits):
338 #Preprocessor is configurable. Make sure to pass in new default for
339 #the enabled flag if one was specified.
340 self.register_preprocessor(preprocessor(parent=self), enabled)
341
342 elif isclass:
343 #Preprocessor is not configurable, construct it
344 self.register_preprocessor(preprocessor(), enabled)
345
346 else:
347 #Preprocessor is an instance of something without a __call__
348 #attribute.
349 raise TypeError('preprocessor')
350
351
352 215 def register_filter(self, name, jinja_filter):
353 216 """
354 217 Register a filter.
@@ -437,24 +300,6 b' class Exporter(LoggingConfigurable):'
437 300 self.environment.comment_end_string = self.jinja_comment_block_end
438 301
439 302
440 def _init_preprocessors(self):
441 """
442 Register all of the preprocessors needed for this exporter, disabled
443 unless specified explicitly.
444 """
445 self._preprocessors = []
446
447 #Load default preprocessors (not necessarly enabled by default).
448 if self.default_preprocessors:
449 for preprocessor in self.default_preprocessors:
450 self.register_preprocessor(preprocessor)
451
452 #Load user preprocessors. Enable by default.
453 if self.preprocessors:
454 for preprocessor in self.preprocessors:
455 self.register_preprocessor(preprocessor, enabled=True)
456
457
458 303 def _init_filters(self):
459 304 """
460 305 Register all of the filters required for the exporter.
@@ -468,53 +313,3 b' class Exporter(LoggingConfigurable):'
468 313 if self.filters:
469 314 for key, user_filter in self.filters.items():
470 315 self.register_filter(key, user_filter)
471
472
473 def _init_resources(self, resources):
474
475 #Make sure the resources dict is of ResourcesDict type.
476 if resources is None:
477 resources = ResourcesDict()
478 if not isinstance(resources, ResourcesDict):
479 new_resources = ResourcesDict()
480 new_resources.update(resources)
481 resources = new_resources
482
483 #Make sure the metadata extension exists in resources
484 if 'metadata' in resources:
485 if not isinstance(resources['metadata'], ResourcesDict):
486 resources['metadata'] = ResourcesDict(resources['metadata'])
487 else:
488 resources['metadata'] = ResourcesDict()
489 if not resources['metadata']['name']:
490 resources['metadata']['name'] = 'Notebook'
491
492 #Set the output extension
493 resources['output_extension'] = self.file_extension
494 return resources
495
496
497 def _preprocess(self, nb, resources):
498 """
499 Preprocess the notebook before passing it into the Jinja engine.
500 To preprocess the notebook is to apply all of the
501
502 Parameters
503 ----------
504 nb : notebook node
505 notebook that is being exported.
506 resources : a dict of additional resources that
507 can be accessed read/write by preprocessors
508 and filters.
509 """
510
511 # Do a copy.deepcopy first,
512 # we are never safe enough with what the preprocessors could do.
513 nbc = copy.deepcopy(nb)
514 resc = copy.deepcopy(resources)
515
516 #Run each preprocessor on the notebook. Carry the output along
517 #to each preprocessor
518 for preprocessor in self._preprocessors:
519 nbc, resc = preprocessor(nbc, resc)
520 return nbc, resc
@@ -18,7 +18,7 b' from IPython.config import Config'
18 18
19 19 from .base import ExportersTestsBase
20 20 from .cheese import CheesePreprocessor
21 from ..exporter import Exporter
21 from ..templateexporter import TemplateExporter
22 22
23 23
24 24 #-----------------------------------------------------------------------------
@@ -31,9 +31,9 b' class TestExporter(ExportersTestsBase):'
31 31
32 32 def test_constructor(self):
33 33 """
34 Can an Exporter be constructed?
34 Can an TemplateExporter be constructed?
35 35 """
36 Exporter()
36 TemplateExporter()
37 37
38 38
39 39 def test_export(self):
@@ -104,5 +104,5 b' class TestExporter(ExportersTestsBase):'
104 104 def _make_exporter(self, config=None):
105 105 #Create the exporter instance, make sure to set a template name since
106 106 #the base Exporter doesn't have a template associated with it.
107 exporter = Exporter(config=config, template_file='python')
107 exporter = TemplateExporter(config=config, template_file='python')
108 108 return exporter
@@ -1,7 +1,7 b''
1 1 from .ansi import *
2 from .citation import *
2 3 from .datatypefilter import *
3 4 from .highlight import *
4 5 from .latex import *
5 6 from .markdown import *
6 7 from .strings import *
7 from .citation import * No newline at end of file
@@ -17,7 +17,7 b' Module with utility functions for preprocessor tests'
17 17 from IPython.nbformat import current as nbformat
18 18
19 19 from ...tests.base import TestsBase
20 from ...exporters.baseexporter import ResourcesDict
20 from ...exporters.exporter import ResourcesDict
21 21
22 22 #-----------------------------------------------------------------------------
23 23 # Class
General Comments 0
You need to be logged in to leave comments. Login now