execute.py 4.5 KB

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