Coverage for databooks/recipes.py: 100%

21 statements  

« prev     ^ index     » next       coverage.py v7.16.2, created at 2026-10-20 15:04 +0200

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

2 

3from dataclasses import dataclass 

4from enum import Enum 

5from typing import Dict 

6 

7 

8@dataclass 

9class RecipeInfo: 

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

11 

12 src: str 

13 description: str 

14 

15 

16@dataclass 

17class CookBook: 

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

19 

20 seq_exec = RecipeInfo( 

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

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

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

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

25 ) 

26 seq_increase = RecipeInfo( 

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

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

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

30 " order.", 

31 ) 

32 has_tags = RecipeInfo( 

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

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

35 ) 

36 has_tags_code = RecipeInfo( 

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

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

39 ) 

40 max_cells = RecipeInfo( 

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

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

43 ) 

44 startswith_md = RecipeInfo( 

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

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

47 ) 

48 no_empty_code = RecipeInfo( 

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

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

51 ) 

52 

53 @classmethod 

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

55 """ 

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

57 

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

59 """ 

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

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

62 

63 

64Recipe = Enum("Recipe", CookBook._recipes())