Coverage for pyquickhelper/helpgen/sphinx_main_verification.py: 74%

58 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-06-03 02:21 +0200

1# -*- coding: utf-8 -*- 

2""" 

3@file 

4@brief Function to verify the files produced by Sphinx 

5""" 

6import os 

7import re 

8import warnings 

9from ..loghelper.flog import noLOG 

10from ..filehelper.synchelper import explore_folder_iterfile 

11 

12 

13class SphinxVerificationException(Exception): 

14 

15 """ 

16 to format the error message 

17 """ 

18 

19 def __init__(self, errors): 

20 """ 

21 @param errors errors met 

22 """ 

23 stack = [] 

24 for name, line, m in errors: 

25 message = f'[sphinxerror]-4 {m}\n File "{name}", line {line + 1}' 

26 stack.append(message) 

27 Exception.__init__(self, "\n" + "\n".join(stack)) 

28 

29 

30def verification_html_format(folder, fLOG=noLOG, raise_above=0.1): 

31 """ 

32 dig into folders abd subfolders to find HTML files 

33 produced by Sphinx, does some verification to detect errors, 

34 the function, the function raises an exception for all mistakes 

35 

36 @param folder folder to verifiy 

37 @param fLOG logging function 

38 @param raise_above raises an exception of the number of errors is above a given threshold 

39 or a relative threshold if it is a float 

40 @return list of errors 

41 """ 

42 nbfile = 0 

43 errors = [] 

44 for item in explore_folder_iterfile(folder, ".[.]html", fullname=True): 

45 fLOG("[verification_html_format]", item) 

46 if not os.path.exists(item): 

47 fLOG( 

48 f"[verification_html_format] unable to find and check '{item}'") 

49 continue 

50 err = verification_html_file(item, fLOG=fLOG) 

51 if len(err) > 0: 

52 fitem = os.path.abspath(item) 

53 if "html/coverage" in fitem.replace("\\", "/"): 

54 # we skip as it comes from coverage report. 

55 pass 

56 else: 

57 errors.extend((fitem, line, m) for line, m in err) 

58 nbfile += 1 

59 fLOG(f"[verification_html_format] checked:{nbfile} errors:{len(errors)}") 

60 if len(errors) > 0: # pragma: no cover 

61 e = SphinxVerificationException(errors) 

62 if isinstance(raise_above, int) and len(errors) >= raise_above: 

63 raise e 

64 if len(errors) * 1.0 / nbfile >= raise_above: 

65 raise e 

66 warnings.warn(f"Sphinx error {e}", UserWarning) 

67 return errors 

68 

69 

70def verification_html_file(item, fLOG=noLOG): 

71 """ 

72 Verifies a file produced by :epkg:`sphinx` and checks basic mistakes. 

73 

74 @param item filename 

75 @param fLOG logging function 

76 @return list of errors (line, message) 

77 

78 The first line is 0. 

79 """ 

80 with open(item, "r", encoding="utf8") as f: 

81 content = f.read() 

82 

83 content = content.replace("\r", "").replace("\n", "_#!#_LINES_#_") 

84 content = re.sub("<pre>(.*?)</pre>", "<pre></pre>", content) 

85 content = content.replace("_#!#_LINES_#_", "\n") 

86 lines = content.split("\n") 

87 reg = re.compile("([.][.] _[-a-z_A-Z0-9][:.])") 

88 

89 errors = [] 

90 for i, line in enumerate(lines): 

91 if "<h1>Source code for " in line or "<h1>Code source de " in line: 

92 # no need to go further 

93 # the source takes place after this substring 

94 break 

95 if ":ref:`" in line and ":ref:`{ref_name}`" not in line: 

96 errors.append((i, "wrong :ref:` in " + line.strip("\n\r "))) 

97 if ":func:`" in line: 

98 errors.append((i, "wrong :func:` in " + line.strip("\n\r "))) 

99 if ":class:`" in line: 

100 errors.append((i, "wrong :class:` in " + line.strip("\n\r "))) 

101 if ":meth:`" in line: 

102 errors.append((i, "wrong :meth:` in " + line.strip("\n\r "))) 

103 if ":method:`" in line: 

104 errors.append((i, "wrong :method:` in " + line.strip("\n\r "))) 

105 if ">`" in line and "`</span></a>-" not in line: 

106 errors.append((i, "wrong >`, missing _ in " + line.strip("\n\r "))) 

107 find = reg.findall(line) 

108 if len(find) > 0: 

109 errors.append( 

110 (i, "label or index remaining: " + str(find) + " in " + line.strip("\n\r "))) 

111 

112 return errors