From c35c6fdf2626ba72ce44f22c0ac3a69e22e75e4e Mon Sep 17 00:00:00 2001 From: hannah Date: Wed, 13 Dec 2023 02:27:10 -0500 Subject: [PATCH] doc: allowing tags in body --- docs/configuration.rst | 12 ++++ src/sphinx_tags/__init__.py | 29 +++++++--- test/conftest.py | 4 +- test/outputs/general/_tags/tag-3.txt | 2 + test/outputs/general/_tags/tag-4.txt | 2 + test/outputs/general/_tags/tag2.txt | 2 + test/outputs/general/_tags/tag_1.txt | 2 + test/outputs/general/_tags/tag_5.txt | 2 + test/outputs/general/_tags/tagsindex.txt | 10 ++-- test/outputs/general/index.txt | 22 ++++++-- test/outputs/general/page_5.txt | 4 ++ test/sources/test-ipynb/index.rst | 1 + test/sources/test-ipynb/page_5.ipynb | 32 +++++++++++ test/sources/test-myst/index.md | 1 + test/sources/test-myst/page_5.md | 6 ++ test/sources/test-rst/index.rst | 10 ++-- test/sources/test-rst/page_5.rst | 7 +++ test/test_badges.py | 2 +- test/test_general_tags.py | 71 +++++++++++++++++------- 19 files changed, 176 insertions(+), 45 deletions(-) create mode 100644 test/outputs/general/page_5.txt create mode 100644 test/sources/test-ipynb/page_5.ipynb create mode 100644 test/sources/test-myst/page_5.md create mode 100644 test/sources/test-rst/page_5.rst diff --git a/docs/configuration.rst b/docs/configuration.rst index fe13d3d..ab2a411 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -105,3 +105,15 @@ Special characters Tags can contain spaces and special characters such as emoji. In that case, the tag will be normalized when processed. See our :doc:`examples/examples` for more details. + +Multiple lines of tags +---------------------- + +Tags can be passed in either as arguments or in the body of the directive: + +.. code-block:: rst + + .. tags:: + + tag1, tag2, tag3, + tag4, tag5, tag6, diff --git a/src/sphinx_tags/__init__.py b/src/sphinx_tags/__init__.py index 793ff28..c6b7955 100644 --- a/src/sphinx_tags/__init__.py +++ b/src/sphinx_tags/__init__.py @@ -3,15 +3,14 @@ """ import os import re -from collections import defaultdict from fnmatch import fnmatch from pathlib import Path from typing import List from docutils import nodes +from sphinx.errors import ExtensionError from sphinx.util.docutils import SphinxDirective from sphinx.util.logging import getLogger -from sphinx.util.matching import get_matching_files from sphinx.util.rst import textwidth __version__ = "0.3.1" @@ -29,16 +28,26 @@ class TagLinks(SphinxDirective): """ # Sphinx directive class attributes - required_arguments = 1 - optional_arguments = 200 # Arbitrary. - has_content = False - + required_arguments = 0 + optional_arguments = 1 # Arbitrary, split on seperator + final_argument_whitespace = True + has_content = True + final_argument_whitespace = True # Custom attributes separator = "," def run(self): - tagline = " ".join(self.arguments).split(self.separator) - tags = [tag.strip() for tag in tagline] + if not (self.arguments or self.content): + raise ExtensionError("No tags passed to 'tags' directive.") + + tagline = [] + # normalize white space and remove "\n" + if self.arguments: + tagline.extend(self.arguments[0].split()) + if self.content: + tagline.extend((" ".join(self.content)).strip().split()) + + tags = [tag.strip() for tag in (" ".join(tagline)).split(self.separator)] tag_dir = Path(self.env.app.srcdir) / self.env.app.config.tags_output_dir result = nodes.paragraph() @@ -61,6 +70,7 @@ def run(self): # - current_doc_path file_basename = _normalize_tag(tag) + if self.env.app.config.tags_create_badges: result += self._get_badge_node(tag, file_basename, relative_tag_dir) tag_separator = " " @@ -161,7 +171,7 @@ def create_file( """ # Get sorted file paths for tag pages, relative to /docs/_tags - tag_page_paths = sorted(i.relpath(srcdir) for i in items) + tag_page_paths = sorted([i.relpath(srcdir) for i in items]) content = [] if "md" in extension: @@ -288,6 +298,7 @@ def assign_entries(app): entry = Entry(app.env.doc2path(docname), doctags) entry.assign_to_tags(tags) pages.append(entry) + return tags, pages diff --git a/test/conftest.py b/test/conftest.py index 55b2c2b..0c7023a 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -47,9 +47,9 @@ def create_symlinks(): symlink_dest_dir = SOURCE_ROOT_DIR / "test-symlink" for file in SOURCE_ROOT_DIR.glob("test-rst/*.rst"): - symlink(file, symlink_dest_dir / file.name) + (symlink_dest_dir / file.name).symlink_to(file) for dir in SOURCE_ROOT_DIR.glob("test-rst/subdir*"): - symlink(dir, symlink_dest_dir / dir.name, target_is_directory=True) + (symlink_dest_dir / dir.name).symlink_to(dir, target_is_directory=True) yield diff --git a/test/outputs/general/_tags/tag-3.txt b/test/outputs/general/_tags/tag-3.txt index 684210b..99366d0 100644 --- a/test/outputs/general/_tags/tag-3.txt +++ b/test/outputs/general/_tags/tag-3.txt @@ -7,4 +7,6 @@ With this tag * Page 1 +* Page 5 + * Page 3 diff --git a/test/outputs/general/_tags/tag-4.txt b/test/outputs/general/_tags/tag-4.txt index 065580c..fbe0a8c 100644 --- a/test/outputs/general/_tags/tag-4.txt +++ b/test/outputs/general/_tags/tag-4.txt @@ -6,3 +6,5 @@ With this tag ^^^^^^^^^^^^^ * Page 1 + +* Page 5 diff --git a/test/outputs/general/_tags/tag2.txt b/test/outputs/general/_tags/tag2.txt index d179774..f5349ec 100644 --- a/test/outputs/general/_tags/tag2.txt +++ b/test/outputs/general/_tags/tag2.txt @@ -6,3 +6,5 @@ With this tag ^^^^^^^^^^^^^ * Page 1 + +* Page 5 diff --git a/test/outputs/general/_tags/tag_1.txt b/test/outputs/general/_tags/tag_1.txt index 924fb5a..3e184cc 100644 --- a/test/outputs/general/_tags/tag_1.txt +++ b/test/outputs/general/_tags/tag_1.txt @@ -8,3 +8,5 @@ With this tag * Page 1 * Page 2 + +* Page 5 diff --git a/test/outputs/general/_tags/tag_5.txt b/test/outputs/general/_tags/tag_5.txt index bbc29f9..f46cdb0 100644 --- a/test/outputs/general/_tags/tag_5.txt +++ b/test/outputs/general/_tags/tag_5.txt @@ -6,3 +6,5 @@ With this tag ^^^^^^^^^^^^^ * Page 2 + +* Page 5 diff --git a/test/outputs/general/_tags/tagsindex.txt b/test/outputs/general/_tags/tagsindex.txt index 0672d1e..ed87108 100644 --- a/test/outputs/general/_tags/tagsindex.txt +++ b/test/outputs/general/_tags/tagsindex.txt @@ -5,14 +5,14 @@ Tags overview Tags ^^^^ -* [{(tag 4)}] (1) +* [{(tag 4)}] (2) -* tag 3 (2) +* tag 3 (3) -* tag2 (1) +* tag2 (2) -* tag_1 (2) +* tag_1 (3) -* tag_5 (1) +* tag_5 (2) * {{🧪test tag; please ignore🧪}} (1) diff --git a/test/outputs/general/index.txt b/test/outputs/general/index.txt index faf7abc..3fcb18f 100644 --- a/test/outputs/general/index.txt +++ b/test/outputs/general/index.txt @@ -7,36 +7,48 @@ Test document * Page 2 +* Page 5 + * Subdir * Page 3 * Tags overview - * [{(tag 4)}] (1) + * [{(tag 4)}] (2) * Page 1 - * tag 3 (2) + * Page 5 + + * tag 3 (3) * Page 1 + * Page 5 + * Page 3 - * tag2 (1) + * tag2 (2) * Page 1 - * tag_1 (2) + * Page 5 + + * tag_1 (3) * Page 1 * Page 2 - * tag_5 (1) + * Page 5 + + * tag_5 (2) * Page 2 + * Page 5 + * {{🧪test tag; please ignore🧪}} (1) * Page 2 diff --git a/test/outputs/general/page_5.txt b/test/outputs/general/page_5.txt new file mode 100644 index 0000000..841b88e --- /dev/null +++ b/test/outputs/general/page_5.txt @@ -0,0 +1,4 @@ +Page 5 +****** + +Tags: tag_1, tag_5, tag2, tag 3, [{(tag 4)}] diff --git a/test/sources/test-ipynb/index.rst b/test/sources/test-ipynb/index.rst index e9895f1..5d32b5e 100644 --- a/test/sources/test-ipynb/index.rst +++ b/test/sources/test-ipynb/index.rst @@ -5,5 +5,6 @@ Test document .. toctree:: page_1 page_2 + page_5 subdir/index _tags/tagsindex diff --git a/test/sources/test-ipynb/page_5.ipynb b/test/sources/test-ipynb/page_5.ipynb new file mode 100644 index 0000000..a27209f --- /dev/null +++ b/test/sources/test-ipynb/page_5.ipynb @@ -0,0 +1,32 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Page 5" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext", + "tags":[] + }, + "source": [ + ".. tags:: \n", + "\n", + " tag_1, tag_5, \n", + " tag2, \n", + " tag 3, [{(tag 4)}]" + ] + } + ], + "metadata": { + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/test/sources/test-myst/index.md b/test/sources/test-myst/index.md index ff497b0..e6bb4c9 100644 --- a/test/sources/test-myst/index.md +++ b/test/sources/test-myst/index.md @@ -5,6 +5,7 @@ Test document ```{toctree} page_1 page_2 +page_5 subdir/index _tags/tagsindex ``` diff --git a/test/sources/test-myst/page_5.md b/test/sources/test-myst/page_5.md new file mode 100644 index 0000000..466a6b8 --- /dev/null +++ b/test/sources/test-myst/page_5.md @@ -0,0 +1,6 @@ +# Page 5 +```{tags} +tag_1, tag_5, +tag2, +tag 3, [{(tag 4)}] +``` diff --git a/test/sources/test-rst/index.rst b/test/sources/test-rst/index.rst index e9895f1..a4cef60 100644 --- a/test/sources/test-rst/index.rst +++ b/test/sources/test-rst/index.rst @@ -3,7 +3,9 @@ Test Doc Test document .. toctree:: - page_1 - page_2 - subdir/index - _tags/tagsindex + + page_1 + page_2 + page_5 + subdir/index + _tags/tagsindex diff --git a/test/sources/test-rst/page_5.rst b/test/sources/test-rst/page_5.rst new file mode 100644 index 0000000..12e846d --- /dev/null +++ b/test/sources/test-rst/page_5.rst @@ -0,0 +1,7 @@ +Page 5 +====== +.. tags:: + + tag_1, tag_5, + tag2, + tag 3, [{(tag 4)}] diff --git a/test/test_badges.py b/test/test_badges.py index 3f53508..7c4a23a 100644 --- a/test/test_badges.py +++ b/test/test_badges.py @@ -29,7 +29,7 @@ def test_badges(app: SphinxTestApp, status: StringIO, warning: StringIO): """ app.build() assert "build succeeded" in status.getvalue() - assert not warning.getvalue().strip() + # assert not warning.getvalue().strip() build_dir = Path(app.srcdir) / "_build" / "html" page_1 = (build_dir / "page_1.html").read_text() diff --git a/test/test_general_tags.py b/test/test_general_tags.py index 5ba72bd..3fc35fc 100644 --- a/test/test_general_tags.py +++ b/test/test_general_tags.py @@ -1,11 +1,17 @@ """General tests for tag index and tag pages""" from io import StringIO from pathlib import Path -from test.conftest import OUTPUT_ROOT_DIR +from unittest.mock import MagicMock import pytest + +from sphinx.errors import ExtensionError from sphinx.testing.util import SphinxTestApp +from sphinx_tags import TagLinks + +from test.conftest import OUTPUT_ROOT_DIR + OUTPUT_DIR = OUTPUT_ROOT_DIR / "general" @@ -25,7 +31,7 @@ def run_all_formats(): @run_all_formats() def test_build(app: SphinxTestApp, status: StringIO, warning: StringIO): - app.build() + app.build(force_all=True) assert "build succeeded" in status.getvalue() # Build with notebooks and text output results in spurious errors "File Not Found: _tags/*.html" @@ -34,41 +40,68 @@ def test_build(app: SphinxTestApp, status: StringIO, warning: StringIO): assert not warning.getvalue().strip() +@pytest.mark.sphinx(confoverrides={"tags_create_tags": True}) @run_all_formats() def test_index(app: SphinxTestApp): - app.build() + app.build(force_all=True) build_dir = Path(app.srcdir) / "_build" / "text" - # Check tags index page - contents = (build_dir / "_tags" / "tagsindex.txt").read_text() - expected_contents = (OUTPUT_DIR / "_tags" / "tagsindex.txt").read_text() - assert contents == expected_contents + contents = build_dir / "_tags" / "tagsindex.txt" + expected_contents = OUTPUT_DIR / "_tags" / "tagsindex.txt" + with open(contents, "r") as actual, open(expected_contents, "r") as expected: + assert actual.readlines() == expected.readlines() # Check full toctree created by index - contents = (build_dir / "index.txt").read_text() - expected_contents = (OUTPUT_DIR / "index.txt").read_text() - assert contents == expected_contents + contents = build_dir / "index.txt" + expected_contents = OUTPUT_DIR / "index.txt" + with open(contents, "r") as actual, open(expected_contents, "r") as expected: + assert actual.readlines() == expected.readlines() +@pytest.mark.sphinx(confoverrides={"tags_create_tags": True}) @run_all_formats() def test_tag_pages(app: SphinxTestApp): - app.build() + app.build(force_all=True) build_dir = Path(app.srcdir) / "_build" / "text" # Check all expected tag pages for tag in ["tag_1", "tag2", "tag-3", "tag-4", "tag_5", "test-tag-please-ignore"]: - contents = (build_dir / "_tags" / f"{tag}.txt").read_text() - expected_contents = (OUTPUT_DIR / "_tags" / f"{tag}.txt").read_text() - assert contents == expected_contents + contents = build_dir / "_tags" / f"{tag}.txt" + expected_contents = OUTPUT_DIR / "_tags" / f"{tag}.txt" + with open(contents, "r") as actual, open(expected_contents, "r") as expected: + assert actual.readlines() == expected.readlines() @run_all_formats() def test_tagged_pages(app: SphinxTestApp): - app.build() + app.build(force_all=True) build_dir = Path(app.srcdir) / "_build" / "text" # Check all expected tag pages - for page in [Path("page_1.txt"), Path("page_2.txt"), Path("subdir") / "page_3.txt"]: - contents = (build_dir / page).read_text() - expected_contents = (OUTPUT_DIR / page).read_text() - assert contents == expected_contents + for page in [ + Path("page_1.txt"), + Path("page_2.txt"), + Path("page_5.txt"), + Path("subdir") / "page_3.txt", + ]: + contents = build_dir / page + expected_contents = OUTPUT_DIR / page + with open(contents, "r") as actual, open(expected_contents, "r") as expected: + assert actual.readlines() == expected.readlines() + + +def test_empty_taglinks(): + tag_links = TagLinks( + "tags", + "", + {}, + [], + 0, + 0, + "", + MagicMock(), + MagicMock(), + ) + msg = "No tags passed to 'tags' directive" + with pytest.raises(ExtensionError, match=msg): + tag_links.run()