1# Copyright (c) IPython Development Team.
2# Distributed under the terms of the Modified BSD License.
3
4"""Module containing a preprocessor that executes the code cells
5and updates outputs"""
6
7from __future__ import annotations
8
9import typing as t
10
11from jupyter_client.manager import KernelManager
12from nbclient.client import NotebookClient
13from nbclient.client import execute as _execute
14
15# Backwards compatibility for imported name
16from nbclient.exceptions import CellExecutionError # noqa: F401
17from nbformat import NotebookNode
18
19from .base import Preprocessor
20
21
22def executenb(*args, **kwargs):
23 """DEPRECATED."""
24 from warnings import warn
25
26 warn(
27 "The 'nbconvert.preprocessors.execute.executenb' function was moved to nbclient.execute. "
28 "We recommend importing that library directly.",
29 FutureWarning,
30 stacklevel=2,
31 )
32 return _execute(*args, **kwargs)
33
34
35# We inherit from both classes to allow for traitlets to resolve as they did pre-6.0.
36# This unfortunately makes for some ugliness around initialization as NotebookClient
37# assumes it's a constructed class with a nb object that we have to hack around.
38class ExecutePreprocessor(Preprocessor, NotebookClient):
39 """
40 Executes all the cells in a notebook
41 """
42
43 def __init__(self, **kw):
44 """Initialize the preprocessor."""
45 nb = kw.get("nb")
46 if nb is None:
47 nb = NotebookNode()
48 Preprocessor.__init__(self, nb=nb, **kw)
49 NotebookClient.__init__(self, nb, **kw)
50
51 def _check_assign_resources(self, resources):
52 if resources or not hasattr(self, "resources"):
53 self.resources = resources
54
55 def preprocess(
56 self, nb: NotebookNode, resources: t.Any = None, km: KernelManager | None = None
57 ) -> tuple[NotebookNode, dict[str, t.Any]]:
58 """
59 Preprocess notebook executing each code cell.
60
61 The input argument *nb* is modified in-place.
62
63 Note that this function recalls NotebookClient.__init__, which may look wrong.
64 However since the preprocess call acts line an init on execution state it's expected.
65 Therefore, we need to capture it here again to properly reset because traitlet
66 assignments are not passed. There is a risk if traitlets apply any side effects for
67 dual init.
68 The risk should be manageable, and this approach minimizes side-effects relative
69 to other alternatives.
70
71 One alternative but rejected implementation would be to copy the client's init internals
72 which has already gotten out of sync with nbclient 0.5 release before nbconvert 6.0 released.
73
74 Parameters
75 ----------
76 nb : NotebookNode
77 Notebook being executed.
78 resources : dictionary (optional)
79 Additional resources used in the conversion process. For example,
80 passing ``{'metadata': {'path': run_path}}`` sets the
81 execution path to ``run_path``.
82 km: KernelManager (optional)
83 Optional kernel manager. If none is provided, a kernel manager will
84 be created.
85
86 Returns
87 -------
88 nb : NotebookNode
89 The executed notebook.
90 resources : dictionary
91 Additional resources used in the conversion process.
92 """
93 NotebookClient.__init__(self, nb, km)
94 self.reset_execution_trackers()
95 self._check_assign_resources(resources)
96
97 with self.setup_kernel():
98 assert self.kc
99 info_msg = self.wait_for_reply(self.kc.kernel_info())
100 assert info_msg
101 self.nb.metadata["language_info"] = info_msg["content"]["language_info"]
102 for index, cell in enumerate(self.nb.cells):
103 self.preprocess_cell(cell, resources, index)
104 self.set_widgets_metadata()
105
106 return self.nb, self.resources
107
108 def preprocess_cell(self, cell, resources, index):
109 """
110 Override if you want to apply some preprocessing to each cell.
111 Must return modified cell and resource dictionary.
112
113 Parameters
114 ----------
115 cell : NotebookNode cell
116 Notebook cell being processed
117 resources : dictionary
118 Additional resources used in the conversion process. Allows
119 preprocessors to pass variables into the Jinja engine.
120 index : int
121 Index of the cell being processed
122 """
123 self._check_assign_resources(resources)
124 cell = self.execute_cell(cell, index, store_history=True)
125 return cell, self.resources