diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 15fc7316e..72ab9d5eb 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -71,7 +71,8 @@ jobs: displayName: 'export' - script: gcc --version displayName: 'gcc version' - - script: brew install p7zip + - script: brew upgrade p7zip + continueOnError: true displayName: 'Install p7zip' - script: brew install pandoc displayName: 'Install Pandoc' diff --git a/src/pyquickhelper/ipythonhelper/notebook_runner.py b/src/pyquickhelper/ipythonhelper/notebook_runner.py index af35e0554..c95b80467 100644 --- a/src/pyquickhelper/ipythonhelper/notebook_runner.py +++ b/src/pyquickhelper/ipythonhelper/notebook_runner.py @@ -1,6 +1,7 @@ """ @file -@brief Modified version of `runipy.notebook_runner `_. +@brief Modified version of `runipy.notebook_runner +`_. """ import base64 @@ -127,10 +128,11 @@ def __init__(self, nb, profile_dir=None, working_dir=None, self.code_init = code_init self._filename = filename if filename is not None else "memory" self.replacements = replacements - self.init_args = dict(profile_dir=profile_dir, working_dir=working_dir, - comment=comment, fLOG=fLOG, theNotebook=theNotebook, code_init=code_init, - kernel_name="python", log_level="30", extended_args=None, - kernel=kernel, filename=filename, replacements=replacements) + self.init_args = dict( + profile_dir=profile_dir, working_dir=working_dir, + comment=comment, fLOG=fLOG, theNotebook=theNotebook, code_init=code_init, + kernel_name="python", log_level="30", extended_args=None, + kernel=kernel, filename=filename, replacements=replacements) args = [] if profile_dir: @@ -161,7 +163,7 @@ def __init__(self, nb, profile_dir=None, working_dir=None, "ignore", category=ResourceWarning) self.km.start_kernel(extra_arguments=args) except Exception as e: # pragma: no cover - raise Exception( + raise NotebookKernelError( "Failure with args: {0}\nand error:\n{1}".format(args, str(e))) from e if platform.system() == 'Darwin': @@ -313,14 +315,15 @@ def get_cell_code(cell): except AttributeError: # pragma: no cover return iscell, cell.input - def run_cell(self, index_cell, cell, clean_function=None): + def run_cell(self, index_cell, cell, clean_function=None, max_nbissue=15): ''' Runs a notebook cell and update the output of that cell inplace. - @param index_cell index of the cell - @param cell cell to execute - @param clean_function cleaning function to apply to the code before running it - @return output of the cell + :param index_cell: index of the cell + :param cell: cell to execute + :param clean_function: cleaning function to apply to the code before running it + :param max_nbissue: number of times an issue can be raised before stopping + :return: output of the cell ''' if self.detailed_log: self.detailed_log("[run_cell] index_cell={0} clean_function={1}".format( @@ -361,8 +364,8 @@ def run_cell(self, index_cell, cell, clean_function=None): tr = [ansi_escape.sub('', _) for _ in reply['content']['traceback']] except KeyError: # pragma: no cover - tr = ["No traceback, available keys in reply['content']"] + \ - list(reply['content']) + tr = (["No traceback, available keys in reply['content']"] + + list(reply['content'])) traceback_text = '\n'.join(tr) self.fLOG("[nberror]\n", traceback_text) if self.detailed_log: @@ -374,19 +377,25 @@ def run_cell(self, index_cell, cell, clean_function=None): outs = list() nbissue = 0 + statuses = [status] while True: try: msg = self.kc.get_iopub_msg(timeout=1) if msg['msg_type'] == 'status': if msg['content']['execution_state'] == 'idle': + status = 'ok' + statuses.append(status) break - except Empty: # pragma: no cover - # execution state should return to idle before the queue becomes empty, + statuses.append(status) + except Empty as e: # pragma: no cover + # execution state should return to idle before + # the queue becomes empty, # if it doesn't, something bad has happened status = "error" + statuses.append(status) reason = "exception Empty was raised" nbissue += 1 - if nbissue > 10: + if nbissue > max_nbissue: # the notebook is empty return "" else: @@ -487,12 +496,17 @@ def reply2string(reply): scode = [code] else: scode = "" - mes = "FILENAME\n{10}:1:1\n{7}\nCELL status={8}, reason={9} -- {4} length={5} -- {6}:\n-----------------\n{0}" + \ - "\n-----------------\nTRACE:\n{1}\nRAW:\n{2}REPLY:\n{3}" + mes = ("FILENAME\n{10}:1:1 - cell:{11}\n{7}\nCELL status={8}, reason='{9}' -- {4} " + "length={5} -- {6}:\n-----------------\n" + "content={12}\nmsg_type: {13} nbissue={14}" + "\nstatuses={15}" + "\n-----------------\n{0}" + "\n-----------------\nTRACE:\n{1}\nRAW:\n{2}REPLY:\n{3}") raise NotebookError(mes.format( - code, traceback_text, sraw, sreply, index_cell, len( - code), scode, self.comment, status, reason, - self._filename)) + code, traceback_text, sraw, sreply, index_cell, # 0-4 + len(code), scode, self.comment, status, reason, # 5-9 + self._filename, index_cell, content, msg_type, nbissue, # 10-14 + statuses)) # 15 if self.detailed_log: self.detailed_log('[run_cell] status={0}'.format(status)) return outs @@ -916,9 +930,10 @@ def run_notebook(self, skip_exceptions=False, progress_callback=None, @param additional_path additional paths (as a list or None if none) @param valid if not None, valid is a function which returns whether or not the cell should be executed or not, if the function - returns None, the execution of the notebooks and skip the execution - of the other cells - @param clean_function function which cleans a cell's code before executing it (None for None) + returns None, the execution of the notebooks and skip + the execution of the other cells + @param clean_function function which cleans a cell's code before executing + it (None for None) @return dictionary with statistics The function adds the local variable ``theNotebook`` with