Execute a Jupyter notebook including inline markdown with nbconvert
我有一个Jupyter笔记本,它在markdown单元格中包含python变量,如下所示:
代码单元:
1 | x = 10 |
降价单元:
1 | The value of x is {{x}}. |
如果我在笔记本中使用shift-enter执行markdown单元,则IPython-notebook-extension Python Markdown允许我动态显示这些变量。
降价单元:
1 | The value of x is 10. |
我想以编程方式执行笔记本中的所有单元格,并使用以下方法将它们保存到新笔记本中:
1 2 3 4 5 6 7 8 9 | import nbformat from nbconvert.preprocessors import ExecutePreprocessor with open('report.ipynb') as f: nb = nbformat.read(f, as_version=4) ep = ExecutePreprocessor(timeout=600, kernel_name='python3') ep.preprocess(nb, {}) with open('report_executed.ipynb', 'wt') as f: nbformat.write(nb, f) |
这将执行代码单元,但不执行降价单元。 他们仍然看起来像这样:
1 | The value of x is {{x}}. |
我认为问题是笔记本电脑不受信任。 有没有办法告诉ExecutePreprocessor信任笔记本? 还有另一种方法可以以编程方式执行Markdown单元中包含python变量的笔记本吗?
ExecutePreprocessor只查看代码单元,因此您的markdown单元完全未受影响。如前所述,要进行降价处理,您需要Python Markdown预处理器。
不幸的是,Python Markdown预处理系统仅在实时笔记本中执行代码,它通过修改与渲染单元格有关的javascript来执行。修改将执行代码段的结果存储在单元元数据中。
但是,根据您的情况,您没有实时笔记本的元数据。我有一个类似的问题,我通过编写自己的执行预处理器解决了这个问题,该预处理器还包括处理减价单元的逻辑:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 | from nbconvert.preprocessors import ExecutePreprocessor, Preprocessor import nbformat, nbconvert from textwrap import dedent class ExecuteCodeMarkdownPreprocessor(ExecutePreprocessor): def __init__(self, **kw): self.sections = {'default': True} # maps section ID to true or false self.EmptyCell = nbformat.v4.nbbase.new_raw_cell("") return super().__init__(**kw) def preprocess_cell(self, cell, resources, cell_index): """ Executes a single code cell. See base.py for details. To execute all cells see :meth:`preprocess`. """ if cell.cell_type not in ['code','markdown']: return cell, resources if cell.cell_type == 'code': # Do code stuff return self.preprocess_code_cell(cell, resources, cell_index) elif cell.cell_type == 'markdown': # Do markdown stuff return self.preprocess_markdown_cell(cell, resources, cell_index) else: # Don't do anything return cell, resources def preprocess_code_cell(self, cell, resources, cell_index): ''' Process code cell. ''' outputs = self.run_cell(cell) cell.outputs = outputs if not self.allow_errors: for out in outputs: if out.output_type == 'error': pattern = u"""\ An error occurred while executing the following cell: ------------------ {cell.source} ------------------ {out.ename}: {out.evalue} """ msg = dedent(pattern).format(out=out, cell=cell) raise nbconvert.preprocessors.execute.CellExecutionError(msg) return cell, resources def preprocess_markdown_cell(self, cell, resources, cell_index): # Find and execute snippets of code cell['metadata']['variables'] = {} for m in re.finditer("{{(.*?)}}", cell.source): # Execute code fakecell = nbformat.v4.nbbase.new_code_cell(m.group(1)) fakecell, resources = self.preprocess_code_cell(fakecell, resources, cell_index) # Output found in cell.outputs # Put output in cell['metadata']['variables'] for output in fakecell.outputs: html = self.convert_output_to_html(output) if html is not None: cell['metadata']['variables'][fakecell.source] = html break return cell, resources def convert_output_to_html(self, output): '''Convert IOpub output to HTML See https://github.com/ipython-contrib/IPython-notebook-extensions/blob/master/nbextensions/usability/python-markdown/main.js ''' if output['output_type'] == 'error': text = '**' + output.ename + '**: ' + output.evalue; return text elif output.output_type == 'execute_result' or output.output_type == 'display_data': data = output.data if 'text/latex' in data: html = data['text/latex'] return html elif 'image/svg+xml' in data: # Not supported #var svg = ul['image/svg+xml']; #/* embed SVG in an <img> tag, still get eaten by sanitizer... */ #svg = btoa(svg); #html = '<img src="data:image/svg+xml;base64,' + svg + '"/>'; return None elif 'image/jpeg' in data: jpeg = data['image/jpeg'] html = '<img src="data:image/jpeg;base64,' + jpeg + '"/>' return html elif 'image/png' in data: png = data['image/png'] html = '<img src="data:image/png;base64,' + png + '"/>' return html elif 'text/markdown' in data: text = data['text/markdown'] return text elif 'text/html' in data: html = data['text/html'] return html elif 'text/plain' in data: text = data['text/plain'] # Strip <p> and </p> tags # Strip quotes # html.match(/<p> ([\s\S]*?)<\/p>/)[1] text = re.sub(r'<p> ([\s\S]*?)<\/p>', r'\1', text) text = re.sub(r"'([\s\S]*?)'",r'\1', text) return text else: # Some tag we don't support return None else: return None |
然后,您可以使用类似于您发布的代码的逻辑处理笔记本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | import nbformat from nbconvert.preprocessors import ExecutePreprocessor import ExecuteCodeMarkdownPreprocessor # from wherever you put it import PyMarkdownPreprocessor # from pre_pymarkdown.py with open('report.ipynb') as f: nb = nbformat.read(f, as_version=4) ep = ExecuteCodeMarkdownPreprocessor(timeout=600, kernel_name='python3') ep.preprocess(nb, {}) pymk = PyMarkdownPreprocessor() pymk.preprocess(nb, {}) with open('report_executed.ipynb', 'wt') as f: nbformat.write(nb, f) |
请注意,通过包含Python Markdown预处理,您生成的笔记本文件将不再在markdown单元格中使用