|
1 | 1 | from __future__ import annotations |
2 | 2 |
|
| 3 | +import json |
| 4 | +import os |
| 5 | +import re |
3 | 6 | import shutil |
4 | 7 | import subprocess |
5 | 8 | import sys |
6 | | -import requests |
| 9 | +import tempfile |
7 | 10 | import webbrowser |
8 | 11 | from itertools import repeat |
9 | 12 | from pathlib import Path |
|
14 | 17 | Tuple, |
15 | 18 | ) |
16 | 19 |
|
17 | | -import re |
18 | 20 | import nox |
19 | 21 | from nox import Session |
| 22 | +from requests import ( |
| 23 | + get, |
| 24 | + head, |
| 25 | +) |
| 26 | +from requests.exceptions import Timeout |
20 | 27 |
|
21 | 28 | from exasol.toolbox.nox._shared import DOCS_OUTPUT_DIR |
22 | 29 | from noxconfig import ( |
23 | 30 | PROJECT_CONFIG, |
24 | 31 | Config, |
25 | 32 | ) |
26 | | -import tempfile |
27 | | -import json |
28 | 33 |
|
29 | 34 |
|
30 | 35 | def _build_docs(session: nox.Session, config: Config) -> None: |
@@ -103,54 +108,107 @@ def clean_docs(_session: Session) -> None: |
103 | 108 | @nox.session(name="docs:links", python=False) |
104 | 109 | def docs_list_links(session: Session) -> None: |
105 | 110 | """List all the links within the documentation.""" |
106 | | - for path, url in _doc_urls(_doc_files(PROJECT_CONFIG.root)): |
107 | | - session.log(f"Url: {url}, File: {path}") |
| 111 | + ignore = [r".*"] |
| 112 | + env = os.environ.copy() |
| 113 | + env["SPHINX_EXTRA_LINKCHECK_IGNORES"] = ",".join(ignore) |
| 114 | + with tempfile.TemporaryDirectory() as path: |
| 115 | + tmpdir = Path(path) |
| 116 | + sp = subprocess.run( |
| 117 | + [ |
| 118 | + "poetry", |
| 119 | + "run", |
| 120 | + "--", |
| 121 | + "sphinx-build", |
| 122 | + "-b", |
| 123 | + "linkcheck", |
| 124 | + PROJECT_CONFIG.root / "doc", |
| 125 | + tmpdir, |
| 126 | + ], |
| 127 | + capture_output=True, |
| 128 | + text=True, |
| 129 | + env=env, |
| 130 | + ) |
| 131 | + print(sp.returncode) |
| 132 | + if sp.returncode >= 2: |
| 133 | + print(sp.stderr) |
| 134 | + session.error(2) |
| 135 | + output = tmpdir / "output.json" |
| 136 | + links = output.read_text().split("\n") |
| 137 | + file_links = [] |
| 138 | + for link in links: |
| 139 | + if link != "": |
| 140 | + line = json.loads(link) |
| 141 | + if not line["uri"].startswith("#"): |
| 142 | + file_links.append(line) |
| 143 | + file_links.sort(key=lambda file: file["filename"]) |
| 144 | + print( |
| 145 | + "\n".join( |
| 146 | + f"filename: {f["filename"]} -> uri: {f["uri"]}" for f in file_links |
| 147 | + ) |
| 148 | + ) |
108 | 149 |
|
109 | 150 |
|
110 | 151 | @nox.session(name="docs:links:check", python=False) |
111 | 152 | def docs_links_check(session: Session) -> None: |
112 | 153 | """Checks whether all links in the documentation are accessible.""" |
113 | | - with tempfile.TemporaryDirectory() as tmpdir: |
114 | | - tmpdir = Path(tmpdir) |
115 | | - sp = subprocess.run(["poetry", "run", "--", "sphinx-build", "-b", 'linkcheck', PROJECT_CONFIG.root/"doc", tmpdir], capture_output=True, text=True) |
| 154 | + ignore = [r"https?://"] |
| 155 | + env = os.environ.copy() |
| 156 | + env["SPHINX_EXTRA_LINKCHECK_IGNORES"] = ",".join(ignore) |
| 157 | + with tempfile.TemporaryDirectory() as path: |
| 158 | + tmpdir = Path(path) |
| 159 | + sp = subprocess.run( |
| 160 | + [ |
| 161 | + "poetry", |
| 162 | + "run", |
| 163 | + "--", |
| 164 | + "sphinx-build", |
| 165 | + "-b", |
| 166 | + "linkcheck", |
| 167 | + PROJECT_CONFIG.root / "doc", |
| 168 | + tmpdir, |
| 169 | + ], |
| 170 | + capture_output=True, |
| 171 | + text=True, |
| 172 | + env=env, |
| 173 | + ) |
116 | 174 | print(sp.returncode) |
117 | 175 | if sp.returncode >= 2: |
118 | 176 | print(sp.stderr) |
119 | 177 | session.error(2) |
120 | | - output = tmpdir/"output.json" |
| 178 | + output = tmpdir / "output.json" |
121 | 179 | results = output.read_text().split("\n") |
122 | 180 | reslen = len(results) |
123 | 181 | resstr = results[-1] |
124 | 182 | if (reslen == 0) or ((reslen == 1) and (resstr == "")): |
125 | 183 | return |
126 | 184 | elif resstr == "": |
127 | 185 | results.pop() |
128 | | - for line, result in enumerate(results): |
| 186 | + for line_nr, result in enumerate(results): |
129 | 187 | resdict = json.loads(result) |
130 | | - if resdict['status'] == 'ignored' and resdict['uri'].startswith('http'): |
| 188 | + if resdict["status"] == "ignored" and resdict["uri"].startswith("http"): |
131 | 189 | try: |
132 | 190 | match = re.search(r"https?://[^\s\"\'<>]+", resdict["uri"]) |
133 | 191 | if match: |
134 | | - resdict['uri'] = match.group() |
135 | | - print(f"{line}/{reslen}") |
136 | | - result = requests.head(resdict['uri'], timeout=5) |
137 | | - if result.status_code != 200: |
138 | | - result = requests.get(resdict['uri'], timeout=5, stream=True) |
139 | | - result.close() |
140 | | - if result.status_code >= 400: |
141 | | - resdict['status'] = 'broken' |
142 | | - resdict['code'] = result.status_code |
143 | | - if result.status_code < 400: |
144 | | - resdict['status'] = 'working' |
145 | | - resdict['code'] = result.status_code |
146 | | - except requests.exceptions.Timeout: |
147 | | - resdict['status'] = 'timeout' |
148 | | - results[line] = json.dumps(resdict) |
| 192 | + resdict["uri"] = match.group() |
| 193 | + print(f"{line_nr}/{reslen}") |
| 194 | + request = head(resdict["uri"], timeout=5) |
| 195 | + if request.status_code != 200: |
| 196 | + request = get(resdict["uri"], timeout=5, stream=True) |
| 197 | + request.close() |
| 198 | + if request.status_code >= 400: |
| 199 | + resdict["status"] = "broken" |
| 200 | + resdict["code"] = request.status_code |
| 201 | + if request.status_code < 400: |
| 202 | + resdict["status"] = "working" |
| 203 | + resdict["code"] = request.status_code |
| 204 | + except Timeout: |
| 205 | + resdict["status"] = "timeout" |
| 206 | + results[line_nr] = json.dumps(resdict) |
149 | 207 | output.write_text("\n".join(f"{r}" for r in results)) |
150 | 208 | errors = [] |
151 | 209 | for result in results: |
152 | | - line = json.loads(result) |
153 | | - if (line["status"] == "broken") or line["status"] == "timeout": |
| 210 | + data = json.loads(result) |
| 211 | + if (data["status"] == "broken") or data["status"] == "timeout": |
154 | 212 | errors.append(result) |
155 | 213 | if errors: |
156 | 214 | print("Error" + "s" if len(errors) > 1 else "") |
|
0 commit comments