Coverage for pycommons / dev / tests / examples_in_md.py: 100%

39 statements  

« prev     ^ index     » next       coverage.py v7.13.0, created at 2025-12-11 03:04 +0000

1"""Test all the `Python` example code in a markdown file.""" 

2from os import getcwd 

3from typing import Final 

4 

5from pycommons.dev.tests.compile_and_run import compile_and_run 

6from pycommons.io.console import logger 

7from pycommons.io.path import Path, directory_path, file_path 

8 

9 

10def check_examples_in_md(file: str) -> None: 

11 """ 

12 Test all the example Python codes in a markdown file. 

13 

14 :param file: the file 

15 :raises TypeError: if `file` is not a string 

16 :raises ValueError: if `file` is empty or otherwise invalid 

17 

18 >>> from contextlib import redirect_stdout 

19 >>> with redirect_stdout(None): 

20 ... check_examples_in_md(file_path(file_path(__file__).up( 

21 ... 4).resolve_inside("README.md"))) 

22 

23 >>> try: 

24 ... check_examples_in_md(1) 

25 ... except TypeError as te: 

26 ... print(te) 

27 descriptor '__len__' requires a 'str' object but received a 'int' 

28 

29 >>> try: 

30 ... check_examples_in_md(None) 

31 ... except TypeError as te: 

32 ... print(te) 

33 descriptor '__len__' requires a 'str' object but received a 'NoneType' 

34 

35 >>> try: 

36 ... check_examples_in_md("") 

37 ... except ValueError as ve: 

38 ... print(ve) 

39 Path must not be empty. 

40 

41 >>> try: 

42 ... check_examples_in_md("/") 

43 ... except ValueError as ve: 

44 ... print(ve) 

45 Path '/' does not identify a file. 

46 """ 

47 # First, we load the README.md file as a single string 

48 readme: Final[Path] = file_path(file) 

49 logger(f"Executing all examples from file {readme!r}.") 

50 text: Final[str] = readme.read_all_str() 

51 logger(f"Read {str.__len__(text)} characters from {readme!r}.") 

52 

53 i2: int = -1 

54 # All examples start and end with ``` after a newline. 

55 mark1: Final[str] = "\n```" 

56 mark2: Final[str] = "python" # python code starts with ```python 

57 

58 wd: Final[str] = directory_path(getcwd()) # current working directory 

59 logger(f"Current working directory is {wd!r}.") 

60 example_cnt: int = 0 

61 

62 while True: 

63 # First, find the starting mark. 

64 i2 += 1 

65 i1 = text.find(mark1, i2) 

66 if i1 <= i2: 

67 break # no starting mark anymore: done 

68 i1 += len(mark1) 

69 i2 = text.find(mark1, i1) 

70 if i2 <= i1: 

71 raise ValueError(f"No end mark for start mark {mark1!r} " 

72 f"at character {i1}?") 

73 

74 orig_fragment: str = text[i1:i2] # get the fragment 

75 fragment: str = str.strip(orig_fragment) 

76 if str.__len__(fragment) <= 0: 

77 raise ValueError(f"Empty fragment {orig_fragment!r} from " 

78 f"{i1} to {i2}?") 

79 i2 += str.__len__(mark1) 

80 if fragment.startswith(mark2): # it is a python fragment 

81 i3 = fragment.find("\n") 

82 if i3 < str.__len__(mark2): 

83 raise ValueError("Did not find newline in stripped " 

84 f"fragment {orig_fragment!r}?") 

85 compile_and_run(fragment[i3 + 1:], f"{readme}:{i1}:{i2}") 

86 example_cnt += 1 

87 

88 if example_cnt <= 0: 

89 raise ValueError(f"No example found in {readme!r}.") 

90 logger( 

91 f"Successfully executed all {example_cnt} examples from {readme!r}.")