Source code for sphinxcontrib.requirements_txt.directive
r"""Directive
=============
"""
import os
import re
from glob import glob
from docutils import nodes
from docutils.nodes import Node
from docutils.statemachine import StringList
from jinja2 import Template
from myst_parser.mdit_to_docutils.sphinx_ import SphinxRenderer
from sphinx.util.docutils import SphinxDirective
from sphinx.util.nodes import nested_parse_with_titles
TEMPLATE_DIR = os.path.join(
os.path.join(os.path.dirname(__file__), "assets"), "jinja2"
)
[docs]
def parse(file: str) -> list[str]:
r"""Parse requirements.txt. ``text`` will be displayed as a url, ``other``
will be displayed normally.
:param file:
:type file: str
:rtype: list[str]
"""
items = []
with open(file) as f:
lines = f.readlines()
for line in lines:
if line.startswith("#!"):
continue
item = {}
line = line.strip()
if line.startswith("#"):
line = line[1:].strip()
item["text"] = line
else:
# ignore trailing comments
line = line.split(" #")[0].strip()
pos = line.find("://")
if pos >= 0:
item["text"] = line
item["url"] = "https" + line[pos:]
else:
captures = re.match(r"[\w-]+", line)
# ignore -r/...
if captures:
item["text"] = captures[0]
# other is `> X.Y.Z`
item["other"] = line.strip(captures[0])
item["url"] = "https://pypi.org/project/" + captures[0]
items += [item]
return items
[docs]
class RequirementsDirective(SphinxDirective):
r"""Requirementsdirective."""
has_content = True
optional_arguments = 0
option_spec = {
"title": str,
"template": str,
}
[docs]
def run(self) -> list[Node]:
r"""Run.
:rtype: list[Node]
"""
template = self.options.get("template")
if isinstance(self.state._renderer, SphinxRenderer):
template_ext = "md"
else:
template_ext = "rst"
if template is None:
template = os.path.join(
TEMPLATE_DIR, f"template.{template_ext}.j2"
)
with open(template) as f:
template_text = f.read()
content = self.content[0]
path = self.content.items[0][0]
file = os.path.join(os.path.dirname(path), content)
fmt = self.options.get("title", self.config["requirements_title"])
new_contents = []
for filename in glob(file, recursive=True):
title = os.path.basename(filename).split(os.path.extsep)[0]
if fmt.find("{title}") >= 0:
title = fmt.format(title=title)
else:
title = fmt
items = parse(filename)
new_content = Template(template_text).render(
title=title, items=items
)
new_contents += [new_content]
final_content = "\n\n".join(new_contents)
string_list = StringList(final_content.splitlines(), source="")
node = nodes.Element()
nested_parse_with_titles(self.state, string_list, node)
return node.children