"""Testing Juypter notebooks.Copied 2023-04-27 from the corresponding module in ixmp."""importosimportsysfromwarningsimportwarnimportpytestnbformat=pytest.importorskip("nbformat")
[docs]defrun_notebook(nb_path,tmp_path,env=None,**kwargs):"""Execute a Jupyter notebook via :mod:`nbclient` and collect output. Parameters ---------- nb_path : os.PathLike The notebook file to execute. tmp_path : os.PathLike A directory in which to create temporary output. env : mapping, optional Execution environment for :mod:`nbclient`. Default: :obj:`os.environ`. kwargs : Keyword arguments for :class:`nbclient.NotebookClient`. Defaults are set for: "allow_errors" Default :data:`False`. If :obj:`True`, the execution always succeeds, and cell output contains exception information rather than code outputs. "kernel_version" Jupyter kernel to use. Default: either "python2" or "python3", matching the current Python major version. .. warning:: Any existing configuration for this kernel on the local system— such as an IPython start-up file—will be executed when the kernel starts. Code that enables GUI features can interfere with :func:`run_notebook`. "timeout" in seconds; default 10. Returns ------- nb : :class:`nbformat.NotebookNode` Parsed and executed notebook. errors : list Any execution errors. """importnbformatfromnbclientimportNotebookClient# Read the notebooknb=nbformat.read(nb_path,as_version=4)# Set default keywordskwargs.setdefault("allow_errors",False)kernel=kwargs.pop("kernel",None)ifkernel:# pragma: no coverwarn('"kernel" keyword argument to run_notebook(); use "kernel_name"',DeprecationWarning,2,)kwargs.setdefault("kernel_name",kernelorf"python{sys.version_info[0]}")kwargs.setdefault("timeout",10)# Set up environmentenv=envoros.environ.copy()env.setdefault("PYDEVD_DISABLE_FILE_VALIDATION","1")# Create a client and use it to execute the notebookclient=NotebookClient(nb,**kwargs,resources=dict(metadata=dict(path=tmp_path)))# Execute the notebook.# `env` is passed from nbclient to jupyter_client.launcher.launch_kernel()client.execute(env=env)# Retrieve error information from cellserrors=[outputforcellinnb.cellsif"outputs"incellforoutputincell["outputs"]ifoutput.output_type=="error"]returnnb,errors
[docs]defget_cell_output(nb,name_or_index,kind="data"):"""Retrieve a cell from `nb` according to its metadata `name_or_index`: The Jupyter notebook format allows specifying a document-wide unique 'name' metadata attribute for each cell: https://nbformat.readthedocs.io/en/latest/format_description.html #cell-metadata Return the cell matching `name_or_index` if :class:`str`; or the cell at the :class:`int` index; or raise :class:`ValueError`. Parameters ---------- kind : str, optional Kind of cell output to retrieve. For 'data', the data in format 'text/plain' is run through :func:`eval`. To retrieve an exception message, use 'evalue'. """ifisinstance(name_or_index,int):cell=nb.cells[name_or_index]else:# pragma: no coverfori,_cellinenumerate(nb.cells):try:if_cell.metadata.jupyter.name==name_or_index:cell=_cellbreakexceptAttributeError:continuetry:result=cell["outputs"][0][kind]exceptNameError:# pragma: no coverraiseValueError(f"no cell named {name_or_index}")else:returneval(result["text/plain"])ifkind=="data"elseresult