Coverage for src/ensae_teaching_cs/automation/notebook_test_helper.py: 86%
125 statements
« prev ^ index » next coverage.py v7.1.0, created at 2023-04-28 06:23 +0200
« prev ^ index » next coverage.py v7.1.0, created at 2023-04-28 06:23 +0200
1# -*- coding: utf-8 -*-
2"""
3@file
4@brief Some automation helpers to test notebooks and check they are still working fine.
5"""
6import os
7import shutil
8from pyquickhelper.loghelper import noLOG
9from pyquickhelper.ipythonhelper import execute_notebook_list, execute_notebook_list_finalize_ut
10from pyquickhelper.ipythonhelper import get_additional_paths as pyq_get_additional_paths
11from pyquickhelper.pycode import get_temp_folder
14def ls_notebooks(subfolder):
15 """
16 Lists the notebooks in a particular subfolder.
18 @param subfolder subfolder (related to this module)
19 @return list of files
20 """
21 this = os.path.abspath(os.path.dirname(__file__))
22 docnote = os.path.join(
23 this,
24 "..",
25 "..",
26 "..",
27 "_doc",
28 "notebooks",
29 subfolder)
30 notes = [
31 os.path.normpath(
32 os.path.join(
33 docnote,
34 _)) for _ in os.listdir(docnote)]
36 keepnote = []
37 for i, note in enumerate(notes):
38 ext = os.path.splitext(note)[-1]
39 if ext != ".ipynb":
40 continue
41 keepnote.append(note)
42 return keepnote
45def get_additional_paths():
46 """
47 Returns a list of paths to add before running the notebooks,
48 paths to :epkg:`pyquickhelper`, :epkg:`pyensae`, :epkg:`pymmails`.
50 @return list of paths
51 """
52 import pyquickhelper
53 import pyensae
54 import pymmails
55 import pymyinstall
56 import jyquickhelper
57 addpath = [os.path.dirname(pyquickhelper.__file__),
58 os.path.dirname(pyensae.__file__),
59 os.path.dirname(pymmails.__file__),
60 os.path.dirname(pymyinstall.__file__),
61 os.path.dirname(jyquickhelper.__file__),
62 os.path.join(os.path.abspath(os.path.dirname(__file__)), ".."),
63 ]
64 try:
65 import mlstatpy
66 addpath.append(os.path.dirname(mlstatpy.__file__))
67 except ImportError:
68 pass
69 addpath = [os.path.normpath(os.path.join(_, "..")) for _ in addpath]
70 return addpath
73def clean_function_1a(code):
74 """
75 Function which cleans cells when unittesting notebooks 1A.
77 @param code cell content
78 @return modified code
79 """
80 code = code.replace(
81 'run_cmd("exemple.xlsx"',
82 'skip_run_cmd("exemple.xlsx"')
84 skip = ["faire une chose avec la probabilité 0.7",
85 "# déclenche une exception",
86 "# pour lancer Excel",
87 "for k in list_exercice_1 :",
88 "return ....",
89 "return [ .... ]",
90 "def __init__(self, ...) :",
91 "dictionnaire_depart.items() [0]",
92 "iterateur(0,10) [0]",
93 "# ...... à remplir",
94 'String.Join(",", a.Select(c=>c.ToString()).ToArray())',
95 "# elle n'existe pas encore",
96 "print(tab[i] + tab[i+1])",
97 "if n = 1:",
98 "clenche une exception",
99 'y = "a" * 3 + 1',
100 "i = list_exercice_1.index(k)",
101 "raise KeyError('Arrêtons-nous...')",
102 ]
103 rep = [("# ...", "pass # "),
104 ("%timeit", "#%timeit"),
105 ('%system "exemple.xlsx"', '#%system "exemple.xlsx"'),
106 ('%system "data.xlsx"', '#%system "data.xlsx"'),
107 ('http://telechargement.insee.fr/fichiersdetail/etatcivil2012/dbase/',
108 'http://www.xavierdupre.fr/enseignement/complements/'),
109 ('https://www.insee.fr/fr/statistiques/fichier/2011542/',
110 'http://www.xavierdupre.fr/enseignement/complements/'),
111 ]
112 spl = ["# ......",
113 "# elle n'existe pas encore",
114 ]
116 for s in skip:
117 if s in code:
118 return ""
120 for s in spl:
121 if s in code:
122 code = code.split(s)[0]
124 for s in rep:
125 code = code.replace(s[0], s[1])
127 return code
130def execute_notebooks(folder, notebooks, filter, clean_function=None,
131 fLOG=noLOG, deepfLOG=noLOG, replacements=None, dump=None,
132 additional_path=None):
133 """
134 Executes a list of notebooks.
136 @param folder folder
137 @param notebooks list of notebooks
138 @param filter function which validates the notebooks to test (True means will be tested)
139 @param clean_function cleaning function to apply to the code before running it
140 @param fLOG logging function
141 @param deepfLOG logging function used to run the notebook
142 @param replacements replacements
143 @param dump see function `execute_notebook_list_finalize_ut
144 <http://www.xavierdupre.fr/app/pyquickhelper/helpsphinx/pyquickhelper/ipythonhelper/run_notebook.html#
145 pyquickhelper.ipythonhelper.run_notebook.execute_notebook_list_finalize_ut>`_
146 @param additional_path additional path to add
147 @return dictionary { notebook_file: (isSuccess, outout) }
149 The signature of function ``filter`` is::
151 def filter(i, filename):
152 return True or False
153 """
155 def valid_cell(cell):
156 if "%system" in cell:
157 return False
158 if "df.plot(...)" in cell:
159 return False
160 if 'df["difference"] = ...' in cell:
161 return False
162 if 'remote_open' in cell:
163 return None
164 if 'blobpassword' in cell:
165 return None
166 if 'String.Join(",", a.Select(c=>c.ToString()).ToArray())' in cell:
167 return False
168 if 'Speech.VocalSynthesis("ENSAE", "fr-FR","","")' in cell:
169 return False
170 if 'Speech.VocalSynthesis(text, lang, voice, filename)' in cell:
171 return False
172 if "%%SPEAK fr-FR" in cell:
173 return False
174 if " noeud tri n'est pas encore défini" in cell:
175 return False
176 if "nuplet[1] = 5" in cell:
177 return False
178 if cell == "dico[0]":
179 return False
180 if cell == "dico[ [4,6] ] = 6":
181 return False
182 return True
184 addpaths = get_additional_paths()
185 if additional_path is not None:
186 addpaths += additional_path
187 if filter:
188 notebooks = [_ for i, _ in enumerate(notebooks) if filter(i, _)]
189 if len(notebooks) == 0:
190 raise ValueError("Empty list of notebooks.")
191 res = execute_notebook_list(folder, notebooks, fLOG=fLOG, clean_function=clean_function,
192 valid=valid_cell, additional_path=addpaths,
193 replacements=replacements)
194 execute_notebook_list_finalize_ut(
195 res, fLOG=fLOG, dump=dump)
196 return res
199def copy_data_file(notebook_folder, filename, dest, fLOG=noLOG):
200 """
201 Copies a data file from a notebook folder to the current folder.
203 @param notebook_folder notebook_folder
204 @param filename filename or list of file names
205 @param dest destination folder
206 @parm fLOG logging function
207 @return copied files
208 """
209 if isinstance(filename, list):
210 return [copy_data_file(notebook_folder, f, dest) for f in filename]
211 else:
212 src = os.path.abspath(os.path.join(os.path.dirname(
213 __file__), "..", "..", "..", "_doc", "notebooks", notebook_folder, filename))
214 if not os.path.exists(src):
215 raise FileNotFoundError(src)
216 if not os.path.exists(dest):
217 raise FileNotFoundError(dest)
218 shutil.copy(src, dest)
219 res = os.path.join(dest, os.path.split(src)[-1])
220 fLOG("copy", res)
221 return res
224def a_test_notebook_runner(filename, name, folder, valid=None, copy_files=None, modules=None, fLOG=noLOG):
225 """
226 Runs and tests a specific list of notebooks.
227 The function raises an exception if the execution fails.
229 @param filename test filename
230 @param name substring to look into notebook filenames
231 @param folder where to look for notebooks
232 @param valid skip cells if valid is False, None for all valid
233 @param copy_files files to copy before running the notebooks.
234 @param modules list of extra dependencies
235 @param fLOG logging function
236 """
237 filename = os.path.abspath(filename)
238 temp = get_temp_folder(filename, f"temp_notebook_123_{name}")
239 doc = os.path.normpath(os.path.join(
240 temp, "..", "..", "..", "_doc", "notebooks", folder))
241 if not os.path.exists(doc):
242 raise FileNotFoundError(doc)
243 keepnote = [os.path.join(doc, _) for _ in os.listdir(
244 doc) if name in _ and ".ipynb" in _ and ".ipynb_checkpoints" not in _]
245 if len(keepnote) == 0:
246 raise AssertionError("No found notebook in '{0}'\n{1}".format(
247 doc, "\n".join(os.listdir(doc))))
249 if copy_files is not None:
250 for name_ in copy_files:
251 dest = os.path.join(temp, name_)
252 dest_dir = os.path.dirname(dest)
253 if not os.path.exists(dest_dir):
254 os.mkdir(dest_dir)
255 src_file = os.path.join(doc, name_)
256 fLOG(
257 f"[a_test_notebook_runner] copy '{src_file}' to '{dest_dir}'.")
258 shutil.copy(src_file, dest_dir)
260 import pyquickhelper
261 import jyquickhelper
262 import pyensae
263 import ensae_teaching_cs
264 base = [jyquickhelper, pyquickhelper, pyensae, ensae_teaching_cs]
265 if modules:
266 base.extend(modules)
267 add_path = pyq_get_additional_paths(base)
268 res = execute_notebook_list(
269 temp, keepnote, additional_path=add_path, valid=valid, fLOG=fLOG)
270 execute_notebook_list_finalize_ut(res, fLOG=fLOG, dump=ensae_teaching_cs)