Skip to content

Commit 547a6ac

Browse files
authored
👌 IMPROVE: validate URL (#24)
Ensure value of `url` keys match regex used by Sphinx to identify them
1 parent d073b8a commit 547a6ac

File tree

6 files changed

+34
-13
lines changed

6 files changed

+34
-13
lines changed

setup.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ project_urls =
2929
[options]
3030
packages = find:
3131
install_requires =
32-
attrs>=19,<21
32+
attrs>=19.2,<21
3333
click~=7.1
3434
pyyaml
3535
sphinx~=3.0

sphinx_external_toc/api.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from typing import Any, Dict, Iterator, List, Optional, Set, Union
44

55
import attr
6-
from attr.validators import deep_iterable, instance_of, optional
6+
from attr.validators import deep_iterable, instance_of, matches_re, optional
77

88

99
class FileItem(str):
@@ -23,7 +23,8 @@ class GlobItem(str):
2323
class UrlItem:
2424
"""A URL in a toctree."""
2525

26-
url: str = attr.ib(validator=instance_of(str))
26+
# regex should match sphinx.util.url_re
27+
url: str = attr.ib(validator=[instance_of(str), matches_re(r".+://.*")])
2728
title: Optional[str] = attr.ib(None, validator=optional(instance_of(str)))
2829

2930

sphinx_external_toc/parsing.py

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -211,12 +211,18 @@ def _parse_doc_item(
211211
f"'{item_key}' and '{other_key}' @ '{toc_path}{items_key}/{item_idx}'"
212212
)
213213

214-
if link_keys == {FILE_KEY}:
215-
items.append(FileItem(item_data[FILE_KEY]))
216-
elif link_keys == {GLOB_KEY}:
217-
items.append(GlobItem(item_data[GLOB_KEY]))
218-
elif link_keys == {URL_KEY}:
219-
items.append(UrlItem(item_data[URL_KEY], item_data.get("title")))
214+
try:
215+
if link_keys == {FILE_KEY}:
216+
items.append(FileItem(item_data[FILE_KEY]))
217+
elif link_keys == {GLOB_KEY}:
218+
items.append(GlobItem(item_data[GLOB_KEY]))
219+
elif link_keys == {URL_KEY}:
220+
items.append(UrlItem(item_data[URL_KEY], item_data.get("title")))
221+
except (ValueError, TypeError) as exc:
222+
exc_arg = exc.args[0] if exc.args else ""
223+
raise MalformedError(
224+
f"item validation @ '{toc_path}{items_key}/{item_idx}': {exc_arg}"
225+
) from exc
220226

221227
# generate toc key-word arguments
222228
keywords = {k: toc_data[k] for k in TOCTREE_OPTIONS if k in toc_data}
@@ -226,16 +232,20 @@ def _parse_doc_item(
226232

227233
try:
228234
toc_item = TocTree(items=items, **keywords)
229-
except TypeError as exc:
230-
raise MalformedError(f"toctree validation @ '{toc_path}'") from exc
235+
except (ValueError, TypeError) as exc:
236+
exc_arg = exc.args[0] if exc.args else ""
237+
raise MalformedError(
238+
f"toctree validation @ '{toc_path}': {exc_arg}"
239+
) from exc
231240
toctrees.append(toc_item)
232241

233242
try:
234243
doc_item = Document(
235244
docname=data[file_key], title=data.get("title"), subtrees=toctrees
236245
)
237-
except TypeError as exc:
238-
raise MalformedError(f"doc validation: {path}") from exc
246+
except (ValueError, TypeError) as exc:
247+
exc_arg = exc.args[0] if exc.args else ""
248+
raise MalformedError(f"doc validation @ '{path}': {exc_arg}") from exc
239249

240250
# list of docs that need to be parsed recursively (and path)
241251
docs_to_be_parsed_list = [
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
root: intro
2+
options:
3+
titlesonly: 2
4+
items:
5+
- file: doc1

tests/_bad_toc_files/bad_url.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
root: index
2+
items:
3+
- url: example.com

tests/test_parsing.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ def test_create_toc_dict(path: Path, data_regression):
2626

2727
TOC_FILES_BAD = list(Path(__file__).parent.joinpath("_bad_toc_files").glob("*.yml"))
2828
ERROR_MESSAGES = {
29+
"bad_option_value.yml": "toctree validation @ '/': 'titlesonly'",
30+
"bad_url.yml": "item validation @ '/items/0': 'url' must match regex",
2931
"empty.yml": "toc is not a mapping:",
3032
"file_and_glob_present.yml": "item contains incompatible keys .* @ '/items/0'",
3133
"list.yml": "toc is not a mapping:",

0 commit comments

Comments
 (0)