-
Notifications
You must be signed in to change notification settings - Fork 9
/
jupyter_config.py
124 lines (98 loc) · 4.7 KB
/
jupyter_config.py
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
122
123
124
from inspect import getsourcelines
from pathlib import Path
from packaging.version import parse as parse_version
import re
import textwrap
from typing import Optional
from jupyter_core.paths import jupyter_config_dir
from traitlets.config import Config
from nbautoexport.utils import __version__, get_logger
logger = get_logger()
def initialize_post_save_hook(c: Config):
# >>> nbautoexport initialize, version=[{{version}}] >>>
try:
import nbautoexport
logger = nbautoexport.get_logger()
logger.debug(f"nbautoexport | Installed version is {nbautoexport.__version__}.")
if callable(c.FileContentsManager.post_save_hook):
logger.info(
"nbautoexport | Existing post_save_hook found. "
"Wrapping it to run nbautoexport's afterwards ..."
)
old_post_save = c.FileContentsManager.post_save_hook
def _post_save(model, os_path, contents_manager):
old_post_save(model=model, os_path=os_path, contents_manager=contents_manager)
nbautoexport.post_save(
model=model, os_path=os_path, contents_manager=contents_manager
)
c.FileContentsManager.post_save_hook = _post_save
else:
c.FileContentsManager.post_save_hook = nbautoexport.post_save
logger.info("nbautoexport | Successfully registered post-save hook.")
except Exception as e:
msg = f"nbautoexport | Failed to register post-save hook due to {type(e).__name__}: {e}"
try:
import jupyter_core.application
jupyter_core.application.JupyterApp.instance().log.error(msg)
except Exception as e2:
import sys
sys.stderr.write(
"nbautoexport | Failed to load JupyterApp logger due to "
f"{type(e2).__name__}: {e2}\n"
)
sys.stderr.write(msg + "\n")
# <<< nbautoexport initialize <<<
pass # need this line for above comment to be included in function source
post_save_hook_initialize_block = textwrap.dedent(
"".join(getsourcelines(initialize_post_save_hook)[0][1:-1]).replace(
r"{{version}}", __version__, 1
)
)
block_regex = re.compile(
r"# >>> nbautoexport initialize.*# <<< nbautoexport initialize <<<\n?",
re.DOTALL, # dot matches newline
)
version_regex = re.compile(r"(?<=# >>> nbautoexport initialize, version=\[).*(?=\] >>>)")
def install_post_save_hook(config_path: Optional[Path] = None):
"""Splices the post save hook into the global Jupyter configuration file"""
if config_path is None:
config_dir = jupyter_config_dir()
config_path = Path(config_dir) / "jupyter_notebook_config.py"
config_path = config_path.expanduser().resolve()
if not config_path.exists():
logger.debug(f"No existing Jupyter configuration detected at {config_path}. Creating...")
config_path.parent.mkdir(exist_ok=True, parents=True)
with config_path.open("w", encoding="utf-8") as fp:
fp.write(post_save_hook_initialize_block)
logger.info("nbautoexport post-save hook installed.")
return
# If config exists, check for existing nbautoexport initialize block and install as appropriate
logger.debug(f"Detected existing Jupyter configuration at {config_path}")
with config_path.open("r", encoding="utf-8") as fp:
config = fp.read()
if block_regex.search(config):
logger.info("Detected existing nbautoexport post-save hook.")
version_match = version_regex.search(config)
if version_match:
existing_version = version_match.group()
logger.debug(f"Existing post-save hook is version {existing_version}")
else:
existing_version = ""
logger.debug("Existing post-save hook predates versioning.")
if parse_version(existing_version) < parse_version(__version__):
logger.info(f"Updating nbautoexport post-save hook with version {__version__}...")
with config_path.open("w", encoding="utf-8") as fp:
# Open as w replaces existing file. We're replacing entire config.
escaped_init = post_save_hook_initialize_block.replace(
"\\", r"\\"
) # escape metachars
fp.write(block_regex.sub(escaped_init, config))
else:
logger.info("No changes made.")
return
else:
logger.info("Installing post-save hook.")
with config_path.open("a") as fp:
# Open as a just appends. We append block at the end of existing file.
fp.write("\n" + post_save_hook_initialize_block)
logger.info("nbautoexport post-save hook installed.")