Skip to content

nbautoexport.export

CopyToSubfolderPostProcessor

Bases: PostProcessorBase

Source code in /home/runner/work/nbautoexport/nbautoexport/nbautoexport/export.py
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
class CopyToSubfolderPostProcessor(PostProcessorBase):
    def __init__(self, subfolder: str, export_format: ExportFormat):
        self.subfolder = subfolder
        self.export_format = export_format
        super(CopyToSubfolderPostProcessor, self).__init__()

    def postprocess(self, input: str):
        """Save converted file to a separate directory, removing cell numbers."""
        if self.subfolder is None:
            return

        input: Path = Path(input)

        new_dir = input.parent / self.subfolder
        new_dir.mkdir(exist_ok=True)
        new_path = new_dir / input.name

        if self.export_format == ExportFormat.pdf:
            # Can't read pdf file as unicode, skip rest of postprocessing and just copy
            input.replace(new_path)
            return

        # Rewrite converted file to new path, removing cell numbers
        with input.open("r", encoding="utf-8") as f:
            text = f.read()
        with new_path.open("w", encoding="utf-8") as f:
            f.write(re.sub(r"\n#\sIn\[(([0-9]+)|(\s))\]:\n{2}", "", text))

        # For some formats, we also need to move the assets directory, for stuff like images
        if self.export_format in FORMATS_WITH_IMAGE_DIR:
            assets_dir = input.parent / f"{input.stem}_files"
            if assets_dir.exists() and assets_dir.is_dir():
                new_assets_dir = new_dir / f"{input.stem}_files"
                new_assets_dir.mkdir(exist_ok=True)
                for asset in assets_dir.iterdir():
                    asset.replace(new_assets_dir / asset.name)
                assets_dir.rmdir()

        input.unlink()

postprocess(input)

Save converted file to a separate directory, removing cell numbers.

Source code in /home/runner/work/nbautoexport/nbautoexport/nbautoexport/export.py
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
def postprocess(self, input: str):
    """Save converted file to a separate directory, removing cell numbers."""
    if self.subfolder is None:
        return

    input: Path = Path(input)

    new_dir = input.parent / self.subfolder
    new_dir.mkdir(exist_ok=True)
    new_path = new_dir / input.name

    if self.export_format == ExportFormat.pdf:
        # Can't read pdf file as unicode, skip rest of postprocessing and just copy
        input.replace(new_path)
        return

    # Rewrite converted file to new path, removing cell numbers
    with input.open("r", encoding="utf-8") as f:
        text = f.read()
    with new_path.open("w", encoding="utf-8") as f:
        f.write(re.sub(r"\n#\sIn\[(([0-9]+)|(\s))\]:\n{2}", "", text))

    # For some formats, we also need to move the assets directory, for stuff like images
    if self.export_format in FORMATS_WITH_IMAGE_DIR:
        assets_dir = input.parent / f"{input.stem}_files"
        if assets_dir.exists() and assets_dir.is_dir():
            new_assets_dir = new_dir / f"{input.stem}_files"
            new_assets_dir.mkdir(exist_ok=True)
            for asset in assets_dir.iterdir():
                asset.replace(new_assets_dir / asset.name)
            assets_dir.rmdir()

    input.unlink()

export_notebook(notebook_path, config)

Export a given notebook file given configuration.

Parameters:

Name Type Description Default
notebook_path Path

path to notebook to export with nbconvert

required
config NbAutoexportConfig

configuration

required
Source code in /home/runner/work/nbautoexport/nbautoexport/nbautoexport/export.py
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
def export_notebook(notebook_path: Path, config: NbAutoexportConfig):
    """Export a given notebook file given configuration.

    Args:
        notebook_path (Path): path to notebook to export with nbconvert
        config (NbAutoexportConfig): configuration
    """
    logger.info(f"nbautoexport | Exporting {notebook_path} ...")
    logger.debug(f"nbautoexport | Using export configuration:\n{config.json(indent=2)}")
    with cleared_argv():
        converter = NbConvertApp()
        converter.log.handlers = logger.handlers
        converter.log.setLevel(logger.level)

        for export_format in config.export_formats:
            if config.organize_by == "notebook":
                subfolder = notebook_path.stem
            elif config.organize_by == "extension":
                subfolder = export_format.value

            converter.postprocessor = CopyToSubfolderPostProcessor(
                subfolder=subfolder, export_format=export_format
            )
            converter.export_format = export_format.value
            converter.initialize()
            converter.notebooks = [str(notebook_path)]
            converter.convert_notebooks()

post_save(model, os_path, contents_manager)

Post-save hook for converting notebooks to other formats using Jupyter nbconvert and saving in a subfolder.

The following arguments are standard for Jupyter post-save hooks. See Jupyter Documentation.

Parameters:

Name Type Description Default
model dict

the model representing the file. See Jupyter documentation.

required
os_path str

the filesystem path to the file just written

required
contents_manager FileContentsManager

FileContentsManager instance that hook is bound to

required
Source code in /home/runner/work/nbautoexport/nbautoexport/nbautoexport/export.py
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
def post_save(model: dict, os_path: str, contents_manager: FileContentsManager):
    """Post-save hook for converting notebooks to other formats using Jupyter nbconvert and saving
    in a subfolder.

    The following arguments are standard for Jupyter post-save hooks. See [Jupyter Documentation](
    https://jupyter-notebook.readthedocs.io/en/stable/extending/savehooks.html).

    Args:
        model (dict): the model representing the file. See [Jupyter documentation](
            https://jupyter-notebook.readthedocs.io/en/stable/extending/contents.html#data-model).
        os_path (str): the filesystem path to the file just written
        contents_manager (FileContentsManager): FileContentsManager instance that hook is bound to
    """
    logger.debug("nbautoexport | Executing nbautoexport.export.post_save ...")
    try:
        # only do this for notebooks
        if model["type"] != "notebook":
            logger.debug(f"nbautoexport | {os_path} is not a notebook. Nothing to do.")
            return

        # only do this if we've added the special indicator file to the working directory
        notebook_path = Path(os_path)
        cwd = notebook_path.parent
        save_progress_indicator = cwd / SAVE_PROGRESS_INDICATOR_FILE
        should_convert = save_progress_indicator.exists()

        if should_convert:
            logger.info(f"nbautoexport | {save_progress_indicator} found. Exporting notebook ...")
            config = NbAutoexportConfig.parse_file(
                path=save_progress_indicator, content_type="application/json", encoding="utf-8"
            )
            export_notebook(notebook_path, config=config)

        else:
            logger.debug(f"nbautoexport | {save_progress_indicator} not found. Nothing to do.")
        logger.debug("nbautoexport | post_save successful.")
    except Exception as e:
        logger.error(f"nbautoexport | post_save failed due to {type(e).__name__}: {e}")