010 hide solution#
gegeben:
Ein Jupyter Notebook als
.ipynb
oder als.md
-Datei.Der Dateiname enthält den Teilstring
LOESUNG
Die Notebooks enthalten Code-Zeilen, die mit dem String
#...
(ACHTUNG: keine Leerzeichen) enden
Gesucht:
Ersetze Code-Zeilen, die mit
#...
enden,duch Code-Zeilen, die – korrekt eingerückt – nur noch aus
# TBD
bestehenschreibe die so “maskierten” Notebooks wieder ‘raus, aber ersetze im Dateinamen den Teilstring
LOESUNG
durch den TeilstringAUTO
.
Beispiel:
Input: Zwei mal Zwei
Output: Zwei mal Zwei
Ausführung#
Da Notebooks von Jupyterbook in alphabetischer Reihenfolge ausgeführt werfen, wird dieses Notebook wegen seinem Namen 010_xxx.ipynb
sehr früh ausgeführt – und das ist gut so, das es neue Notebooks erzeugt, die dann noch im gleichen Durchlauf wiederum von Juypterbook ausgeführt werden.
Allerdings wird dieses Notebook nur dann ausgeführt, wenn es selbst geändert wurde, oder es nicht mehr im Cache verfügbar ist.
Am einfachsten uns sichersten ist es daher, dieses Notebook z.B. in einem Makefile vor der eigentlichen Ausführung von Jupyterbook auzuführen, z.B. so:
python 010_hide-solution.py
Wenn man das so machen will, muss das ipynb-Notebook mit Juyptext z.B. mit Percent-Script gepaart werden.
code#
import glob
import json
import regex as re
import os
verbose = 2
HIDE = True
if verbose >= 1:
print("010 hide solution")
010 hide solution
def hide(string):
def substfn(match):
#print([ match[i] for i in range(len(match.groups()) +1) ])
result = "".join([ match[1], "...", match[4] ])
#print(result)
return result
regex = r'( *)(.*)(#\.\.\.) *(.*)'
return re.sub(regex,
# lambda match: "".join([ match[1], "... # ", match[4] ]),# until 2024-03-20
lambda match: "".join([ match[1], match[4], "# TBD" ]), # since 2024-03-21
string,
0) if ("#..." in string and HIDE) else string
hide("x = [ i for i in range(3) ] #... x = ... ")
'x = ... # TBD'
loesung_files = glob.glob("*LOESUNG*.md") + glob.glob("*LOESUNG*.ipynb")
loesung_to_be_processed = []
for loesung in loesung_files:
auto = "AUTO".join(loesung.split('LOESUNG'))
t_loesung = os.path.getmtime(loesung)
t_auto = os.path.getmtime(auto) if os.path.exists(auto) else 0
if t_loesung > t_auto:
loesung_to_be_processed.append((loesung, auto))
loesung_to_be_processed
[]
md_dict = {}
json_dict = {}
for l_path, a_path in loesung_to_be_processed:
if verbose >= 1: print(f"reading {l_path}")
with open(l_path, 'r') as file:
if l_path.endswith(".ipynb"):
json_dict[a_path] = json.load(file)
elif l_path.endswith(".md"):
md_dict[a_path] = file.readlines()
else:
print("Warning: unknown file type")
md#
md_dict
{}
for auto, notebook in md_dict.items():
source_hide = [ hide(line) for line in notebook ]
with open(auto, 'w') as md_file:
md_file.writelines(source_hide)
if verbose >= 1: print(f"wrote {auto}")
if verbose >= 2: print(source_hide)
ipynb#
json_dict.keys()
dict_keys([])
for auto, notebook in json_dict.items():
for cell in notebook['cells']:
# print(type(cell), "\n", cell)
if cell['cell_type'] == 'code':
source = cell['source']
# print(source)
source_hide = [ hide(line) for line in cell['source'] ]
# print(source_hide)
cell['source'] = source_hide
with open(auto, 'w') as json_file:
json.dump(notebook, json_file)
if verbose >= 1: print(f"wrote {auto}")