1"""Module containing single call export functions."""
2
3# Copyright (c) Jupyter Development Team.
4# Distributed under the terms of the Modified BSD License.
5
6import os
7import sys
8
9if sys.version_info < (3, 10):
10 from importlib_metadata import entry_points # type:ignore[import-not-found]
11else:
12 from importlib.metadata import entry_points
13from nbformat import NotebookNode
14from traitlets.config import get_config
15from traitlets.log import get_logger
16from traitlets.utils.importstring import import_item
17
18from .exporter import Exporter
19
20# -----------------------------------------------------------------------------
21# Functions
22# -----------------------------------------------------------------------------
23
24__all__ = [
25 "export",
26 "Exporter",
27 "get_exporter",
28 "get_export_names",
29 "ExporterNameError",
30]
31
32
33class ExporterNameError(NameError):
34 """An exporter name error."""
35
36
37class ExporterDisabledError(ValueError):
38 """An exporter disabled error."""
39
40
41def export(exporter, nb, **kw):
42 """
43 Export a notebook object using specific exporter class.
44
45 Parameters
46 ----------
47 exporter : ``Exporter`` class or instance
48 Class or instance of the exporter that should be used. If the
49 method initializes its own instance of the class, it is ASSUMED that
50 the class type provided exposes a constructor (``__init__``) with the same
51 signature as the base Exporter class.
52 nb : :class:`~nbformat.NotebookNode`
53 The notebook to export.
54 config : config (optional, keyword arg)
55 User configuration instance.
56 resources : dict (optional, keyword arg)
57 Resources used in the conversion process.
58
59 Returns
60 -------
61 tuple
62 output : str
63 The resulting converted notebook.
64 resources : dictionary
65 Dictionary of resources used prior to and during the conversion
66 process.
67 """
68
69 # Check arguments
70 if exporter is None:
71 msg = "Exporter is None"
72 raise TypeError(msg)
73 if not isinstance(exporter, Exporter) and not issubclass(exporter, Exporter):
74 msg = "exporter does not inherit from Exporter (base)"
75 raise TypeError(msg)
76 if nb is None:
77 msg = "nb is None"
78 raise TypeError(msg)
79
80 # Create the exporter
81 resources = kw.pop("resources", None)
82 exporter_instance = exporter if isinstance(exporter, Exporter) else exporter(**kw)
83
84 # Try to convert the notebook using the appropriate conversion function.
85 if isinstance(nb, NotebookNode):
86 output, resources = exporter_instance.from_notebook_node(nb, resources)
87 elif isinstance(nb, (str,)):
88 output, resources = exporter_instance.from_filename(nb, resources)
89 else:
90 output, resources = exporter_instance.from_file(nb, resources)
91 return output, resources
92
93
94def get_exporter(name, config=get_config()): # noqa: B008
95 """Given an exporter name or import path, return a class ready to be instantiated
96
97 Raises ExporterName if exporter is not found or ExporterDisabledError if not enabled
98 """
99
100 if name == "ipynb":
101 name = "notebook"
102
103 try:
104 exporters = entry_points(group="nbconvert.exporters")
105 items = [e for e in exporters if e.name == name or e.name == name.lower()]
106 exporter = items[0].load()
107 if getattr(exporter(config=config), "enabled", True):
108 return exporter
109 raise ExporterDisabledError('Exporter "%s" disabled in configuration' % (name))
110 except IndexError:
111 pass
112
113 if "." in name:
114 try:
115 exporter = import_item(name)
116 if getattr(exporter(config=config), "enabled", True):
117 return exporter
118 raise ExporterDisabledError('Exporter "%s" disabled in configuration' % (name))
119 except ImportError:
120 log = get_logger()
121 log.error("Error importing %s", name, exc_info=True) # noqa: G201
122
123 msg = 'Unknown exporter "{}", did you mean one of: {}?'.format(
124 name, ", ".join(get_export_names())
125 )
126 raise ExporterNameError(msg)
127
128
129def get_export_names(config=get_config()): # noqa: B008
130 """Return a list of the currently supported export targets
131
132 Exporters can be found in external packages by registering
133 them as an nbconvert.exporter entrypoint.
134 """
135 exporters = sorted(e.name for e in entry_points(group="nbconvert.exporters"))
136 if os.environ.get("NBCONVERT_DISABLE_CONFIG_EXPORTERS"):
137 get_logger().info(
138 "Config exporter loading disabled, no additional exporters will be automatically included."
139 )
140 return exporters
141
142 enabled_exporters = []
143 for exporter_name in exporters:
144 try:
145 e = get_exporter(exporter_name)(config=config)
146 if e.enabled:
147 enabled_exporters.append(exporter_name)
148 except (ExporterDisabledError, ValueError):
149 pass
150 return enabled_exporters