Created
October 21, 2023 16:12
-
-
Save AnonymerNiklasistanonym/6a1c17c28e01d1c9388d45b6c7fdd1fd to your computer and use it in GitHub Desktop.
Format Python code cells in Jupyter Notebook (*.ipynb) file/s using black
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import argparse | |
import black | |
import json | |
import re | |
from pathlib import Path | |
def ipynb_format_code_cells(path: Path, verbose: bool) -> tuple[Path, int]: | |
json_data = {} | |
formatted_cells_count = 0 | |
with open(path, "r") as f: | |
data = json.load(f) | |
if "cells" not in data: | |
raise RuntimeError("The attribute 'cells' was not found!") | |
for cell in data["cells"]: | |
if "cell_type" not in cell: | |
raise RuntimeError("The attribute 'cell_type' was not found!") | |
if cell["cell_type"] != "code": | |
continue | |
if "source" not in cell: | |
raise RuntimeError("The attribute 'source' was not found!") | |
cell_str = "".join(cell["source"]) | |
cell_str_formatted = black.format_str(cell_str, mode=black.Mode()).rstrip() | |
if cell_str != cell_str_formatted: | |
if verbose: | |
print(f"Black formatted {cell_str!r} to {cell_str_formatted!r}") | |
formatted_cells_count += 1 | |
cell["source"] = re.split(r"(?<=[\n])", cell_str_formatted) | |
if verbose: | |
print(f"Updated {cell_str!r} with {''.join(cell['source'])!r}") | |
if formatted_cells_count > 0: | |
with open(path, "w") as f: | |
f.write(json.dumps(data, indent=" ", ensure_ascii=False) + "\n") | |
return path, formatted_cells_count | |
def main(paths: list[Path], verbose: bool): | |
results: list[tuple[Path, int]] = [] | |
for path in paths: | |
results.append(ipynb_format_code_cells(path, verbose)) | |
formatted_files = 0 | |
for path, formatted_cells_count in results: | |
if formatted_cells_count > 0: | |
formatted_files += 1 | |
print( | |
f"reformatted {path} ({formatted_cells_count} cell{'s' if formatted_cells_count > 0 else ''})" | |
) | |
unchanged_files = len(paths) - formatted_files | |
if formatted_files > 0: | |
print( | |
f"{formatted_files} file{'s' if formatted_files > 1 else ''} reformatted{', ' if unchanged_files > 0 else '.'}", | |
end="" if unchanged_files > 0 else None, | |
) | |
if unchanged_files > 0: | |
print( | |
f"{unchanged_files} file{'s' if unchanged_files > 1 else ''} left unchanged." | |
) | |
if __name__ == "__main__": | |
parser = argparse.ArgumentParser( | |
description="Format the python code in a jupyter notebook (*.ipynb)" | |
) | |
parser.add_argument("-v", "--version", action="version", version="%(prog)s 0.1") | |
parser.add_argument( | |
"notebook", | |
type=Path, | |
nargs="+", | |
help="file path to the jupyter notebook (*.ipynb)", | |
) | |
parser.add_argument( | |
"--verbose", help="increase output verbosity", action="store_true" | |
) | |
args = parser.parse_args() | |
main(args.notebook, args.verbose) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment