Coverage for databooks/recipes.py: 100%

21 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-10-03 12:27 +0000

1"""Recipe on `databooks assert ...`.""" 

2from dataclasses import dataclass 

3from enum import Enum 

4from typing import Dict 

5 

6 

7@dataclass 

8class RecipeInfo: 

9 """Common evaluation strings for `databooks assert ...`.""" 

10 

11 src: str 

12 description: str 

13 

14 

15@dataclass 

16class CookBook: 

17 """Common user recipes for notebook checks.""" 

18 

19 seq_exec = RecipeInfo( 

20 src="[c.execution_count for c in exec_cells] ==" 

21 " list(range(1, len(exec_cells) + 1))", 

22 description="Assert that the executed code cells were executed sequentially" 

23 " (similar effect to when you 'restart kernel and run all cells').", 

24 ) 

25 seq_increase = RecipeInfo( 

26 src="[c.execution_count for c in exec_cells] ==" 

27 " sorted([c.execution_count for c in exec_cells])", 

28 description="Assert that the executed code cells were executed in increasing" 

29 " order.", 

30 ) 

31 has_tags = RecipeInfo( 

32 src="any(getattr(cell.metadata, 'tags', []) for cell in nb.cells)", 

33 description="Assert that there is at least one cell with tags.", 

34 ) 

35 has_tags_code = RecipeInfo( 

36 src="any(getattr(cell.metadata, 'tags', []) for cell in code_cells)", 

37 description="Assert that there is at least one code cell with tags.", 

38 ) 

39 max_cells = RecipeInfo( 

40 src="len(nb.cells) < 64", 

41 description="Assert that there are less than 64 cells in the notebook.", 

42 ) 

43 startswith_md = RecipeInfo( 

44 src="nb.cells[0].cell_type == 'markdown'", 

45 description="Assert that the first cell in notebook is a markdown cell.", 

46 ) 

47 no_empty_code = RecipeInfo( 

48 src="all(cell.source for cell in code_cells)", 

49 description="Assert that there are no empty code cells in the notebook.", 

50 ) 

51 

52 @classmethod 

53 def _recipes(cls) -> Dict[str, str]: 

54 """ 

55 Get the recipes as a {`recipe-value`: `recipe-name`} dictionary. 

56 

57 `Typer` expects the user to pass `Enum.value`, not `Enum.name`. 

58 """ 

59 names = (attr for attr in dir(cls) if not attr.startswith("_")) 

60 return {getattr(cls, name).src: name.replace("_", "-") for name in names} 

61 

62 

63# https://github.com/python/mypy/issues/5317 

64Recipe = Enum("Recipe", CookBook._recipes()) # type: ignore[misc]