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
« 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
13class SphinxVerificationException(Exception):
15 """
16 to format the error message
17 """
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))
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
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
70def verification_html_file(item, fLOG=noLOG):
71 """
72 Verifies a file produced by :epkg:`sphinx` and checks basic mistakes.
74 @param item filename
75 @param fLOG logging function
76 @return list of errors (line, message)
78 The first line is 0.
79 """
80 with open(item, "r", encoding="utf8") as f:
81 content = f.read()
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][:.])")
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 ")))
112 return errors