Skip to content

Commit

Permalink
tags stashed on env
Browse files Browse the repository at this point in the history
and using arguments, which normalizes spaces
  • Loading branch information
story645 committed Jan 1, 2024
1 parent c4df648 commit 5c56b6e
Show file tree
Hide file tree
Showing 8 changed files with 53 additions and 57 deletions.
61 changes: 22 additions & 39 deletions src/sphinx_tags/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"""
import os
import re
from collections import defaultdict
from fnmatch import fnmatch
from pathlib import Path
from typing import List
Expand Down Expand Up @@ -36,16 +37,18 @@ class TagLinks(SphinxDirective):
separator = ","

def run(self):
# Undo splitting args by whitespace, and use our own separator (to support tags with spaces)
tags = " ".join(self.arguments).split(self.separator)
tags = [t.strip() for t in tags]
tagline = " ".join(self.arguments).split(self.separator)
tags = [tag.strip() for tag in tagline]

tag_dir = Path(self.env.app.srcdir) / self.env.app.config.tags_output_dir
result = nodes.paragraph()
result["classes"] = ["tags"]
result += nodes.inline(text=f"{self.env.app.config.tags_intro_text} ")
count = 0

current_doc_dir = Path(self.env.doc2path(self.env.docname)).parent
relative_tag_dir = Path(os.path.relpath(tag_dir, current_doc_dir))

for tag in tags:
count += 1
# We want the link to be the path to the _tags folder, relative to
Expand All @@ -56,10 +59,8 @@ def run(self):
# - subfolder
# |
# - current_doc_path
current_doc_dir = Path(self.env.doc2path(self.env.docname)).parent
relative_tag_dir = Path(os.path.relpath(tag_dir, current_doc_dir))
file_basename = _normalize_tag(tag)

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 = " "
Expand All @@ -68,6 +69,10 @@ def run(self):
tag_separator = f"{self.separator} "
if not count == len(tags):
result += nodes.inline(text=tag_separator)

# register tags to global metadata for document
self.env.metadata[self.env.docname]["tags"] = tags

return [result]

def _get_plaintext_node(
Expand Down Expand Up @@ -193,30 +198,11 @@ def create_file(


class Entry:
"""Extracted info from source file (*.rst/*.md/*.ipynb)"""
"""Tags to pages map"""

def __init__(self, entrypath: Path):
def __init__(self, entrypath: Path, tags: list):
self.filepath = entrypath
self.lines = self.filepath.read_text(encoding="utf8").split("\n")
if self.filepath.suffix == ".rst":
tagstart = ".. tags::"
tagend = ""
elif self.filepath.suffix == ".md":
tagstart = "```{tags}"
tagend = "```"
elif self.filepath.suffix == ".ipynb":
tagstart = '".. tags::'
tagend = '"'
else:
raise ValueError(
"Unknown file extension. Currently, only .rst, .md .ipynb are supported."
)
tagline = [line for line in self.lines if tagstart in line]
self.tags = []
if tagline:
tagline = tagline[0].replace(tagstart, "").rstrip(tagend)
self.tags = tagline.split(",")
self.tags = [tag.strip() for tag in self.tags]
self.tags = tags

def assign_to_tags(self, tag_dict):
"""Append ourself to tags"""
Expand Down Expand Up @@ -245,6 +231,7 @@ def tagpage(tags, outdir, title, extension, tags_index_head):
This page contains a list of all available tags.
"""

tags = list(tags.values())

if "md" in extension:
Expand Down Expand Up @@ -294,24 +281,18 @@ def assign_entries(app):
pages = []
tags = {}

# Get document paths in the project that match specified file extensions
doc_paths = get_matching_files(
app.srcdir,
include_patterns=[f"**.{extension}" for extension in app.config.tags_extension],
exclude_patterns=app.config.exclude_patterns,
)

for path in doc_paths:
entry = Entry(Path(app.srcdir) / path)
for docname in app.env.found_docs:
doctags = app.env.metadata[docname].get("tags", None)
if doctags is None:
continue # skip if no tags
entry = Entry(app.env.doc2path(docname), doctags)
entry.assign_to_tags(tags)
pages.append(entry)

return tags, pages


def update_tags(app):
"""Update tags according to pages found"""

if app.config.tags_create_tags:
tags_output_dir = Path(app.config.tags_output_dir)

Expand All @@ -324,6 +305,7 @@ def update_tags(app):

# Create pages for each tag
tags, pages = assign_entries(app)

for tag in tags.values():
tag.create_file(
[item for item in pages if tag.name in item.tags],
Expand Down Expand Up @@ -386,4 +368,5 @@ def setup(app):
"version": __version__,
"parallel_read_safe": True,
"parallel_write_safe": True,
"env_version": 1,
}
13 changes: 8 additions & 5 deletions test/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@

import pytest
import sphinx.testing
import sphinx.testing.path

collect_ignore = ["sources", "outputs"]
pytest_plugins = "sphinx.testing.fixtures"
Expand All @@ -31,11 +30,15 @@ def rootdir():
resolved and copied as files.
"""

def copytree(src, dest):
shutil.copytree(src, dest, symlinks=True)
# patch copytree into Path objects to have sphinx 5 compatibility and
class PatchedPath(type(Path())):
def __new__(cls, *pathsegments):
return super().__new__(cls, *pathsegments)

with patch.object(sphinx.testing.path.path, "copytree", copytree):
yield sphinx.testing.path.path(SOURCE_ROOT_DIR)
def copytree(src, dest):
shutil.copytree(src, dest, symlinks=True)

yield PatchedPath(SOURCE_ROOT_DIR)


@pytest.fixture(scope="session", autouse=True)
Expand Down
4 changes: 2 additions & 2 deletions test/outputs/general/_tags/tag-4.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
My tags: [{(tag 4)}]
**********************
My tags: [{(tag 4)}]
********************


With this tag
Expand Down
2 changes: 1 addition & 1 deletion test/outputs/general/_tags/tagsindex.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Tags overview
Tags
^^^^

* [{(tag 4)}] (1)
* [{(tag 4)}] (1)

* tag 3 (2)

Expand Down
2 changes: 1 addition & 1 deletion test/outputs/general/index.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Test document

* Tags overview

* [{(tag 4)}] (1)
* [{(tag 4)}] (1)

* Page 1

Expand Down
2 changes: 1 addition & 1 deletion test/sources/test-badges/conf.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
extensions = ["sphinx_design", "sphinx_tags", "myst_parser"]
extensions = ["sphinx_tags", "myst_parser", "sphinx_design"]
tags_create_tags = True
tags_extension = ["md"]
tags_create_badges = True
Expand Down
23 changes: 16 additions & 7 deletions test/test_badges.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,17 @@
EXPECTED_CLASSES = {
"tag-1": "sd-bg-primary",
"tag-2": "sd-bg-secondary",
"prefix-tag-3": "sd-bg-info",
"tag-4": "sd-bg-dark",
"prefix:tag-3": "sd-bg-info",
"tag 4": "sd-bg-dark",
}


@pytest.mark.sphinx("html", testroot="badges")
def test_build(app: SphinxTestApp, status: StringIO, warning: StringIO):
app.build()
assert "build succeeded" in status.getvalue()


@pytest.mark.sphinx("html", testroot="badges")
def test_badges(app: SphinxTestApp, status: StringIO, warning: StringIO):
"""Parse output HTML for a page with badges, find badge links, and check for CSS classes for
Expand All @@ -27,11 +33,14 @@ def test_badges(app: SphinxTestApp, status: StringIO, warning: StringIO):

build_dir = Path(app.srcdir) / "_build" / "html"
page_1 = (build_dir / "page_1.html").read_text()
assert page_1 is not None
soup = BeautifulSoup(page_1, "html.parser")
# print(soup.prettify())
assert soup is not None
print(soup)
badge_links = soup.find_all("span", class_="sd-badge")
print("badge: ", badge_links)

badge_links = soup.find_all("a", class_="sd-badge")
classes_by_tag = {Path(a.get("href")).stem: a.get("class") for a in badge_links}

for tag, cls in EXPECTED_CLASSES.items():
assert cls in classes_by_tag[tag]
for (tag, class_), span in zip(EXPECTED_CLASSES.items(), badge_links):
assert tag in span.text # hard to test tag 4 b/c of the icon
assert class_ in span["class"]
3 changes: 2 additions & 1 deletion test/test_general_tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ def run_all_formats():
pytest.param(marks=pytest.mark.sphinx("text", testroot="symlink")),
pytest.param(marks=pytest.mark.sphinx("text", testroot="ipynb")),
],
ids=["myst", "rst", "symlink", "ipynb"],
)


Expand All @@ -29,7 +30,7 @@ def test_build(app: SphinxTestApp, status: StringIO, warning: StringIO):

# Build with notebooks and text output results in spurious errors "File Not Found: _tags/*.html"
# For all other formats, ensure no warnings are raised
if not app.srcdir.endswith("ipynb"):
if not str(app.srcdir).endswith("ipynb"):
assert not warning.getvalue().strip()


Expand Down

0 comments on commit 5c56b6e

Please sign in to comment.