muutils.nbutils.convert_ipynb_to_script
fast conversion of Jupyter Notebooks to scripts, with some basic and hacky filtering and formatting.
1"""fast conversion of Jupyter Notebooks to scripts, with some basic and hacky filtering and formatting.""" 2 3from __future__ import annotations 4 5import argparse 6import json 7import os 8from pathlib import Path 9import sys 10import typing 11import warnings 12 13from muutils.spinner import SpinnerContext 14 15DISABLE_PLOTS: dict[str, list[str]] = { 16 "matplotlib": [ 17 """ 18# ------------------------------------------------------------ 19# Disable matplotlib plots, done during processing by `convert_ipynb_to_script.py` 20import matplotlib.pyplot as plt 21plt.show = lambda: None 22# ------------------------------------------------------------ 23""" 24 ], 25 "circuitsvis": [ 26 """ 27# ------------------------------------------------------------ 28# Disable circuitsvis plots, done during processing by `convert_ipynb_to_script.py` 29from circuitsvis.utils.convert_props import PythonProperty, convert_props 30from circuitsvis.utils.render import RenderedHTML, render, render_cdn, render_local 31 32def new_render( 33 react_element_name: str, 34 **kwargs: PythonProperty 35) -> RenderedHTML: 36 "return a visualization as raw HTML" 37 local_src = render_local(react_element_name, **kwargs) 38 cdn_src = render_cdn(react_element_name, **kwargs) 39 # return as string instead of RenderedHTML for CI 40 return str(RenderedHTML(local_src, cdn_src)) 41 42render = new_render 43# ------------------------------------------------------------ 44""" 45 ], 46 "muutils": [ 47 """import muutils.nbutils.configure_notebook as nb_conf 48nb_conf.CONVERSION_PLOTMODE_OVERRIDE = "ignore" 49""" 50 ], 51} 52 53DISABLE_PLOTS_WARNING: list[str] = [ 54 """ 55# ------------------------------------------------------------ 56# WARNING: this script is auto-generated by `convert_ipynb_to_script.py` 57# showing plots has been disabled, so this is presumably in a temp dict for CI or something 58# so don't modify this code, it will be overwritten! 59# ------------------------------------------------------------ 60""".lstrip() 61] 62 63 64def disable_plots_in_script(script_lines: list[str]) -> list[str]: 65 """Disable plots in a script by adding cursed things after the import statements""" 66 result_str_TEMP: str = "\n\n".join(script_lines) 67 script_lines_new: list[str] = script_lines 68 69 if "muutils" in result_str_TEMP: 70 script_lines_new = DISABLE_PLOTS["muutils"] + script_lines_new 71 72 if "matplotlib" in result_str_TEMP: 73 assert ( 74 "import matplotlib.pyplot as plt" in result_str_TEMP 75 ), "matplotlib.pyplot must be imported as plt" 76 77 # find the last import statement involving matplotlib, and the first line that uses plt 78 mpl_last_import_index: int = -1 79 mpl_first_usage_index: int = -1 80 for i, line in enumerate(script_lines_new): 81 if "matplotlib" in line and (("import" in line) or ("from" in line)): 82 mpl_last_import_index = i 83 84 if "configure_notebook" in line: 85 mpl_last_import_index = i 86 87 if "plt." in line: 88 mpl_first_usage_index = i 89 90 assert ( 91 mpl_last_import_index != -1 92 ), f"matplotlib imports not found! see line {mpl_last_import_index}" 93 if mpl_first_usage_index != -1: 94 assert ( 95 mpl_first_usage_index > mpl_last_import_index 96 ), f"matplotlib plots created before import! see lines {mpl_first_usage_index}, {mpl_last_import_index}" 97 else: 98 warnings.warn( 99 "could not find where matplotlib is used, plot disabling might not work!" 100 ) 101 102 # insert the cursed things 103 script_lines_new = ( 104 script_lines_new[: mpl_last_import_index + 1] 105 + DISABLE_PLOTS["matplotlib"] 106 + script_lines_new[mpl_last_import_index + 1 :] 107 ) 108 result_str_TEMP = "\n\n".join(script_lines_new) 109 110 if "circuitsvis" in result_str_TEMP: 111 # find the last import statement involving circuitsvis, and the first line that uses it 112 cirv_last_import_index: int = -1 113 cirv_first_usage_index: int = -1 114 115 for i, line in enumerate(script_lines_new): 116 if "circuitsvis" in line: 117 if (("import" in line) or ("from" in line)) and "circuitsvis" in line: 118 cirv_last_import_index = i 119 else: 120 cirv_first_usage_index = i 121 122 if "configure_notebook" in line: 123 mpl_last_import_index = i 124 125 if "render" in line: 126 cirv_first_usage_index = i 127 128 assert ( 129 cirv_last_import_index != -1 130 ), f"circuitsvis imports not found! see line {cirv_last_import_index}" 131 if cirv_first_usage_index != -1: 132 assert ( 133 cirv_first_usage_index > cirv_last_import_index 134 ), f"circuitsvis plots created before import! see lines {cirv_first_usage_index}, {cirv_last_import_index}" 135 else: 136 warnings.warn( 137 "could not find where circuitsvis is used, plot disabling might not work!" 138 ) 139 140 # insert the cursed things 141 script_lines_new = ( 142 script_lines_new[: cirv_last_import_index + 1] 143 + DISABLE_PLOTS["circuitsvis"] 144 + script_lines_new[cirv_last_import_index + 1 :] 145 ) 146 result_str_TEMP = "\n\n".join(script_lines_new) 147 148 return script_lines_new 149 150 151def convert_ipynb( 152 notebook: dict, 153 strip_md_cells: bool = False, 154 header_comment: str = r"#%%", 155 disable_plots: bool = False, 156 filter_out_lines: str | typing.Sequence[str] = ( 157 "%", 158 "!", 159 ), # ignore notebook magic commands and shell commands 160) -> str: 161 """Convert Jupyter Notebook to a script, doing some basic filtering and formatting. 162 163 # Arguments 164 - `notebook: dict`: Jupyter Notebook loaded as json. 165 - `strip_md_cells: bool = False`: Remove markdown cells from the output script. 166 - `header_comment: str = r'#%%'`: Comment string to separate cells in the output script. 167 - `disable_plots: bool = False`: Disable plots in the output script. 168 - `filter_out_lines: str|typing.Sequence[str] = ('%', '!')`: comment out lines starting with these strings (in code blocks). 169 if a string is passed, it will be split by char and each char will be treated as a separate filter. 170 171 # Returns 172 - `str`: Converted script. 173 """ 174 175 if isinstance(filter_out_lines, str): 176 filter_out_lines = tuple(filter_out_lines) 177 filter_out_lines_set: set = set(filter_out_lines) 178 179 result: list[str] = [] 180 181 all_cells: list[dict] = notebook["cells"] 182 183 for cell in all_cells: 184 cell_type: str = cell["cell_type"] 185 186 if not strip_md_cells and cell_type == "markdown": 187 result.append(f'{header_comment}\n"""\n{"".join(cell["source"])}\n"""') 188 elif cell_type == "code": 189 source: list[str] = cell["source"] 190 if filter_out_lines: 191 source = [ 192 ( 193 f"#{line}" 194 if any( 195 line.startswith(filter_prefix) 196 for filter_prefix in filter_out_lines_set 197 ) 198 else line 199 ) 200 for line in source 201 ] 202 result.append(f'{header_comment}\n{"".join(source)}') 203 204 if disable_plots: 205 result = disable_plots_in_script(result) 206 result = DISABLE_PLOTS_WARNING + result 207 208 return "\n\n".join(result) 209 210 211def process_file( 212 in_file: str, 213 out_file: str | None = None, 214 strip_md_cells: bool = False, 215 header_comment: str = r"#%%", 216 disable_plots: bool = False, 217 filter_out_lines: str | typing.Sequence[str] = ("%", "!"), 218): 219 print(f"\tProcessing {in_file}...", file=sys.stderr) 220 assert os.path.exists(in_file), f"File {in_file} does not exist." 221 assert os.path.isfile(in_file), f"Path {in_file} is not a file." 222 assert in_file.endswith(".ipynb"), f"File {in_file} is not a Jupyter Notebook." 223 224 with open(in_file, "r") as file: 225 notebook: dict = json.load(file) 226 227 try: 228 converted_script: str = convert_ipynb( 229 notebook=notebook, 230 strip_md_cells=strip_md_cells, 231 header_comment=header_comment, 232 disable_plots=disable_plots, 233 filter_out_lines=filter_out_lines, 234 ) 235 except AssertionError as e: 236 print(f"Error converting {in_file}: {e}", file=sys.stderr) 237 raise e 238 239 if out_file: 240 with open(out_file, "w") as file: 241 file.write(converted_script) 242 else: 243 print(converted_script) 244 245 246def process_dir( 247 input_dir: typing.Union[str, Path], 248 output_dir: typing.Union[str, Path], 249 strip_md_cells: bool = False, 250 header_comment: str = r"#%%", 251 disable_plots: bool = False, 252 filter_out_lines: str | typing.Sequence[str] = ("%", "!"), 253): 254 """Convert all Jupyter Notebooks in a directory to scripts. 255 256 # Arguments 257 - `input_dir: str`: Input directory. 258 - `output_dir: str`: Output directory. 259 - `strip_md_cells: bool = False`: Remove markdown cells from the output script. 260 - `header_comment: str = r'#%%'`: Comment string to separate cells in the output script. 261 - `disable_plots: bool = False`: Disable plots in the output script. 262 - `filter_out_lines: str|typing.Sequence[str] = ('%', '!')`: comment out lines starting with these strings (in code blocks). 263 if a string is passed, it will be split by char and each char will be treated as a separate filter. 264 """ 265 266 assert os.path.exists(input_dir), f"Directory {input_dir} does not exist." 267 assert os.path.isdir(input_dir), f"Path {input_dir} is not a directory." 268 269 if not os.path.exists(output_dir): 270 os.makedirs(output_dir, exist_ok=True) 271 272 filenames: list[str] = [ 273 fname for fname in os.listdir(input_dir) if fname.endswith(".ipynb") 274 ] 275 276 assert filenames, f"Directory {input_dir} does not contain any Jupyter Notebooks." 277 n_files: int = len(filenames) 278 print(f"Converting {n_files} notebooks:", file=sys.stderr) 279 280 with SpinnerContext( 281 spinner_chars="braille", 282 update_interval=0.01, 283 format_string_when_updated=True, 284 output_stream=sys.stderr, 285 ) as spinner: 286 for idx, fname in enumerate(filenames): 287 spinner.update_value(f"\tConverting {idx+1}/{n_files}: {fname}") 288 in_file: str = os.path.join(input_dir, fname) 289 out_file: str = os.path.join(output_dir, fname.replace(".ipynb", ".py")) 290 291 with open(in_file, "r", encoding="utf-8") as file_in: 292 notebook: dict = json.load(file_in) 293 294 try: 295 converted_script: str = convert_ipynb( 296 notebook=notebook, 297 strip_md_cells=strip_md_cells, 298 header_comment=header_comment, 299 disable_plots=disable_plots, 300 filter_out_lines=filter_out_lines, 301 ) 302 except AssertionError as e: 303 spinner.stop() 304 raise Exception(f"Error converting {in_file}") from e 305 306 with open(out_file, "w", encoding="utf-8") as file_out: 307 file_out.write(converted_script) 308 309 310if __name__ == "__main__": 311 parser = argparse.ArgumentParser( 312 description="Convert Jupyter Notebook to a script with cell separators." 313 ) 314 parser.add_argument( 315 "in-path", 316 type=str, 317 help="Input Jupyter Notebook file (.ipynb) or directory of files.", 318 ) 319 parser.add_argument( 320 "--out-file", 321 type=str, 322 help="Output script file. If not specified, the result will be printed to stdout.", 323 ) 324 parser.add_argument( 325 "--output-dir", type=str, help="Output directory for converted script files." 326 ) 327 parser.add_argument( 328 "--strip-md-cells", 329 action="store_true", 330 help="Remove markdown cells from the output script.", 331 ) 332 parser.add_argument( 333 "--header-comment", 334 type=str, 335 default=r"#%%", 336 help="Comment string to separate cells in the output script.", 337 ) 338 parser.add_argument( 339 "--disable-plots", 340 action="store_true", 341 help="Disable plots in the output script. Useful for testing in CI.", 342 ) 343 parser.add_argument( 344 "--filter-out-lines", 345 type=str, 346 default="%", 347 help="Comment out lines starting with these characters.", 348 ) 349 350 args = parser.parse_args() 351 352 if args.output_dir: 353 assert not args.out_file, "Cannot specify both --out_file and --output_dir." 354 process_dir( 355 input_dir=args.in_path, 356 output_dir=args.output_dir, 357 strip_md_cells=args.strip_md_cells, 358 header_comment=args.header_comment, 359 disable_plots=args.disable_plots, 360 filter_out_lines=args.filter_out_lines, 361 ) 362 363 else: 364 process_file( 365 in_file=args.in_path, 366 out_file=args.out_file, 367 strip_md_cells=args.strip_md_cells, 368 header_comment=args.header_comment, 369 disable_plots=args.disable_plots, 370 filter_out_lines=args.filter_out_lines, 371 ) 372 373 374print("muutils.nbutils.convert_ipynb_to_script.py loaded.")
DISABLE_PLOTS: dict[str, list[str]] =
{'matplotlib': ['\n# ------------------------------------------------------------\n# Disable matplotlib plots, done during processing by `convert_ipynb_to_script.py`\nimport matplotlib.pyplot as plt\nplt.show = lambda: None\n# ------------------------------------------------------------\n'], 'circuitsvis': ['\n# ------------------------------------------------------------\n# Disable circuitsvis plots, done during processing by `convert_ipynb_to_script.py`\nfrom circuitsvis.utils.convert_props import PythonProperty, convert_props\nfrom circuitsvis.utils.render import RenderedHTML, render, render_cdn, render_local\n\ndef new_render(\n react_element_name: str,\n **kwargs: PythonProperty\n) -> RenderedHTML:\n "return a visualization as raw HTML"\n local_src = render_local(react_element_name, **kwargs)\n cdn_src = render_cdn(react_element_name, **kwargs)\n # return as string instead of RenderedHTML for CI\n return str(RenderedHTML(local_src, cdn_src))\n\nrender = new_render\n# ------------------------------------------------------------\n'], 'muutils': ['import muutils.nbutils.configure_notebook as nb_conf\nnb_conf.CONVERSION_PLOTMODE_OVERRIDE = "ignore"\n']}
DISABLE_PLOTS_WARNING: list[str] =
["# ------------------------------------------------------------\n# WARNING: this script is auto-generated by `convert_ipynb_to_script.py`\n# showing plots has been disabled, so this is presumably in a temp dict for CI or something\n# so don't modify this code, it will be overwritten!\n# ------------------------------------------------------------\n"]
def
disable_plots_in_script(script_lines: list[str]) -> list[str]:
65def disable_plots_in_script(script_lines: list[str]) -> list[str]: 66 """Disable plots in a script by adding cursed things after the import statements""" 67 result_str_TEMP: str = "\n\n".join(script_lines) 68 script_lines_new: list[str] = script_lines 69 70 if "muutils" in result_str_TEMP: 71 script_lines_new = DISABLE_PLOTS["muutils"] + script_lines_new 72 73 if "matplotlib" in result_str_TEMP: 74 assert ( 75 "import matplotlib.pyplot as plt" in result_str_TEMP 76 ), "matplotlib.pyplot must be imported as plt" 77 78 # find the last import statement involving matplotlib, and the first line that uses plt 79 mpl_last_import_index: int = -1 80 mpl_first_usage_index: int = -1 81 for i, line in enumerate(script_lines_new): 82 if "matplotlib" in line and (("import" in line) or ("from" in line)): 83 mpl_last_import_index = i 84 85 if "configure_notebook" in line: 86 mpl_last_import_index = i 87 88 if "plt." in line: 89 mpl_first_usage_index = i 90 91 assert ( 92 mpl_last_import_index != -1 93 ), f"matplotlib imports not found! see line {mpl_last_import_index}" 94 if mpl_first_usage_index != -1: 95 assert ( 96 mpl_first_usage_index > mpl_last_import_index 97 ), f"matplotlib plots created before import! see lines {mpl_first_usage_index}, {mpl_last_import_index}" 98 else: 99 warnings.warn( 100 "could not find where matplotlib is used, plot disabling might not work!" 101 ) 102 103 # insert the cursed things 104 script_lines_new = ( 105 script_lines_new[: mpl_last_import_index + 1] 106 + DISABLE_PLOTS["matplotlib"] 107 + script_lines_new[mpl_last_import_index + 1 :] 108 ) 109 result_str_TEMP = "\n\n".join(script_lines_new) 110 111 if "circuitsvis" in result_str_TEMP: 112 # find the last import statement involving circuitsvis, and the first line that uses it 113 cirv_last_import_index: int = -1 114 cirv_first_usage_index: int = -1 115 116 for i, line in enumerate(script_lines_new): 117 if "circuitsvis" in line: 118 if (("import" in line) or ("from" in line)) and "circuitsvis" in line: 119 cirv_last_import_index = i 120 else: 121 cirv_first_usage_index = i 122 123 if "configure_notebook" in line: 124 mpl_last_import_index = i 125 126 if "render" in line: 127 cirv_first_usage_index = i 128 129 assert ( 130 cirv_last_import_index != -1 131 ), f"circuitsvis imports not found! see line {cirv_last_import_index}" 132 if cirv_first_usage_index != -1: 133 assert ( 134 cirv_first_usage_index > cirv_last_import_index 135 ), f"circuitsvis plots created before import! see lines {cirv_first_usage_index}, {cirv_last_import_index}" 136 else: 137 warnings.warn( 138 "could not find where circuitsvis is used, plot disabling might not work!" 139 ) 140 141 # insert the cursed things 142 script_lines_new = ( 143 script_lines_new[: cirv_last_import_index + 1] 144 + DISABLE_PLOTS["circuitsvis"] 145 + script_lines_new[cirv_last_import_index + 1 :] 146 ) 147 result_str_TEMP = "\n\n".join(script_lines_new) 148 149 return script_lines_new
Disable plots in a script by adding cursed things after the import statements
def
convert_ipynb( notebook: dict, strip_md_cells: bool = False, header_comment: str = '#%%', disable_plots: bool = False, filter_out_lines: Union[str, Sequence[str]] = ('%', '!')) -> str:
152def convert_ipynb( 153 notebook: dict, 154 strip_md_cells: bool = False, 155 header_comment: str = r"#%%", 156 disable_plots: bool = False, 157 filter_out_lines: str | typing.Sequence[str] = ( 158 "%", 159 "!", 160 ), # ignore notebook magic commands and shell commands 161) -> str: 162 """Convert Jupyter Notebook to a script, doing some basic filtering and formatting. 163 164 # Arguments 165 - `notebook: dict`: Jupyter Notebook loaded as json. 166 - `strip_md_cells: bool = False`: Remove markdown cells from the output script. 167 - `header_comment: str = r'#%%'`: Comment string to separate cells in the output script. 168 - `disable_plots: bool = False`: Disable plots in the output script. 169 - `filter_out_lines: str|typing.Sequence[str] = ('%', '!')`: comment out lines starting with these strings (in code blocks). 170 if a string is passed, it will be split by char and each char will be treated as a separate filter. 171 172 # Returns 173 - `str`: Converted script. 174 """ 175 176 if isinstance(filter_out_lines, str): 177 filter_out_lines = tuple(filter_out_lines) 178 filter_out_lines_set: set = set(filter_out_lines) 179 180 result: list[str] = [] 181 182 all_cells: list[dict] = notebook["cells"] 183 184 for cell in all_cells: 185 cell_type: str = cell["cell_type"] 186 187 if not strip_md_cells and cell_type == "markdown": 188 result.append(f'{header_comment}\n"""\n{"".join(cell["source"])}\n"""') 189 elif cell_type == "code": 190 source: list[str] = cell["source"] 191 if filter_out_lines: 192 source = [ 193 ( 194 f"#{line}" 195 if any( 196 line.startswith(filter_prefix) 197 for filter_prefix in filter_out_lines_set 198 ) 199 else line 200 ) 201 for line in source 202 ] 203 result.append(f'{header_comment}\n{"".join(source)}') 204 205 if disable_plots: 206 result = disable_plots_in_script(result) 207 result = DISABLE_PLOTS_WARNING + result 208 209 return "\n\n".join(result)
Convert Jupyter Notebook to a script, doing some basic filtering and formatting.
Arguments
- `notebook: dict`: Jupyter Notebook loaded as json.
- `strip_md_cells: bool = False`: Remove markdown cells from the output script.
- `header_comment: str = r'#%%'`: Comment string to separate cells in the output script.
- `disable_plots: bool = False`: Disable plots in the output script.
- `filter_out_lines: str|typing.Sequence[str] = ('%', '!')`: comment out lines starting with these strings (in code blocks).
if a string is passed, it will be split by char and each char will be treated as a separate filter.
Returns
- `str`: Converted script.
def
process_file( in_file: str, out_file: str | None = None, strip_md_cells: bool = False, header_comment: str = '#%%', disable_plots: bool = False, filter_out_lines: Union[str, Sequence[str]] = ('%', '!')):
212def process_file( 213 in_file: str, 214 out_file: str | None = None, 215 strip_md_cells: bool = False, 216 header_comment: str = r"#%%", 217 disable_plots: bool = False, 218 filter_out_lines: str | typing.Sequence[str] = ("%", "!"), 219): 220 print(f"\tProcessing {in_file}...", file=sys.stderr) 221 assert os.path.exists(in_file), f"File {in_file} does not exist." 222 assert os.path.isfile(in_file), f"Path {in_file} is not a file." 223 assert in_file.endswith(".ipynb"), f"File {in_file} is not a Jupyter Notebook." 224 225 with open(in_file, "r") as file: 226 notebook: dict = json.load(file) 227 228 try: 229 converted_script: str = convert_ipynb( 230 notebook=notebook, 231 strip_md_cells=strip_md_cells, 232 header_comment=header_comment, 233 disable_plots=disable_plots, 234 filter_out_lines=filter_out_lines, 235 ) 236 except AssertionError as e: 237 print(f"Error converting {in_file}: {e}", file=sys.stderr) 238 raise e 239 240 if out_file: 241 with open(out_file, "w") as file: 242 file.write(converted_script) 243 else: 244 print(converted_script)
def
process_dir( input_dir: Union[str, pathlib.Path], output_dir: Union[str, pathlib.Path], strip_md_cells: bool = False, header_comment: str = '#%%', disable_plots: bool = False, filter_out_lines: Union[str, Sequence[str]] = ('%', '!')):
247def process_dir( 248 input_dir: typing.Union[str, Path], 249 output_dir: typing.Union[str, Path], 250 strip_md_cells: bool = False, 251 header_comment: str = r"#%%", 252 disable_plots: bool = False, 253 filter_out_lines: str | typing.Sequence[str] = ("%", "!"), 254): 255 """Convert all Jupyter Notebooks in a directory to scripts. 256 257 # Arguments 258 - `input_dir: str`: Input directory. 259 - `output_dir: str`: Output directory. 260 - `strip_md_cells: bool = False`: Remove markdown cells from the output script. 261 - `header_comment: str = r'#%%'`: Comment string to separate cells in the output script. 262 - `disable_plots: bool = False`: Disable plots in the output script. 263 - `filter_out_lines: str|typing.Sequence[str] = ('%', '!')`: comment out lines starting with these strings (in code blocks). 264 if a string is passed, it will be split by char and each char will be treated as a separate filter. 265 """ 266 267 assert os.path.exists(input_dir), f"Directory {input_dir} does not exist." 268 assert os.path.isdir(input_dir), f"Path {input_dir} is not a directory." 269 270 if not os.path.exists(output_dir): 271 os.makedirs(output_dir, exist_ok=True) 272 273 filenames: list[str] = [ 274 fname for fname in os.listdir(input_dir) if fname.endswith(".ipynb") 275 ] 276 277 assert filenames, f"Directory {input_dir} does not contain any Jupyter Notebooks." 278 n_files: int = len(filenames) 279 print(f"Converting {n_files} notebooks:", file=sys.stderr) 280 281 with SpinnerContext( 282 spinner_chars="braille", 283 update_interval=0.01, 284 format_string_when_updated=True, 285 output_stream=sys.stderr, 286 ) as spinner: 287 for idx, fname in enumerate(filenames): 288 spinner.update_value(f"\tConverting {idx+1}/{n_files}: {fname}") 289 in_file: str = os.path.join(input_dir, fname) 290 out_file: str = os.path.join(output_dir, fname.replace(".ipynb", ".py")) 291 292 with open(in_file, "r", encoding="utf-8") as file_in: 293 notebook: dict = json.load(file_in) 294 295 try: 296 converted_script: str = convert_ipynb( 297 notebook=notebook, 298 strip_md_cells=strip_md_cells, 299 header_comment=header_comment, 300 disable_plots=disable_plots, 301 filter_out_lines=filter_out_lines, 302 ) 303 except AssertionError as e: 304 spinner.stop() 305 raise Exception(f"Error converting {in_file}") from e 306 307 with open(out_file, "w", encoding="utf-8") as file_out: 308 file_out.write(converted_script)
Convert all Jupyter Notebooks in a directory to scripts.
Arguments
- `input_dir: str`: Input directory.
- `output_dir: str`: Output directory.
- `strip_md_cells: bool = False`: Remove markdown cells from the output script.
- `header_comment: str = r'#%%'`: Comment string to separate cells in the output script.
- `disable_plots: bool = False`: Disable plots in the output script.
- `filter_out_lines: str|typing.Sequence[str] = ('%', '!')`: comment out lines starting with these strings (in code blocks).
if a string is passed, it will be split by char and each char will be treated as a separate filter.