|
1 | 1 | from collections.abc import Callable |
| 2 | +import hashlib |
2 | 3 | import os |
3 | 4 | from pathlib import Path |
4 | 5 | from typing import Any, ClassVar, cast |
|
7 | 8 | from docutils.parsers.rst import directives |
8 | 9 | from packaging.version import Version |
9 | 10 | import sphinx |
| 11 | +from sphinx.config import Config as _SphinxConfig |
10 | 12 | from sphinx.util.docutils import SphinxDirective |
11 | 13 | from sphinx_needs.api import add_need # type: ignore[import-untyped] |
12 | 14 | from sphinx_needs.utils import add_doc # type: ignore[import-untyped] |
|
33 | 35 | logger = logging.getLogger(__name__) |
34 | 36 |
|
35 | 37 |
|
| 38 | +def _check_id( |
| 39 | + config: _SphinxConfig, |
| 40 | + id: str | None, |
| 41 | + src_strings: list[str], |
| 42 | + options: dict[str, str], |
| 43 | + extra_options: dict[str, str], |
| 44 | +) -> None: |
| 45 | + """Check and set the id for the need. |
| 46 | +
|
| 47 | + src_strings[0] is always the title. |
| 48 | + src_strings[1] is always the project. |
| 49 | + """ |
| 50 | + if config.needs_id_required: |
| 51 | + if id: |
| 52 | + extra_options["id"] = id |
| 53 | + else: |
| 54 | + if "directory" in options: |
| 55 | + src_strings.append(options["directory"]) |
| 56 | + if "file" in options: |
| 57 | + src_strings.append(options["file"]) |
| 58 | + |
| 59 | + extra_options["id"] = _make_hashed_id("SRCTRACE_", src_strings, config) |
| 60 | + |
| 61 | + |
| 62 | +def _make_hashed_id( |
| 63 | + type_prefix: str, src_strings: list[str], config: _SphinxConfig |
| 64 | +) -> str: |
| 65 | + """Create an ID based on the type and title of the need.""" |
| 66 | + full_title = src_strings[0] # title is always the first element |
| 67 | + hashable_content = "_".join(src_strings) |
| 68 | + hashed = hashlib.sha256(hashable_content.encode("UTF-8")).hexdigest().upper() |
| 69 | + if config.needs_id_from_title: |
| 70 | + hashed = full_title.upper().replace(" ", "_") + "_" + hashed |
| 71 | + return f"{type_prefix}{hashed[: config.needs_id_length]}" |
| 72 | + |
| 73 | + |
36 | 74 | def get_rel_path(doc_path: Path, code_path: Path, base_dir: Path) -> tuple[Path, Path]: |
37 | 75 | """Get the relative path from the document to the source code file and vice versa.""" |
38 | 76 | doc_depth = len(doc_path.parents) - 1 |
@@ -110,8 +148,9 @@ def run(self) -> list[nodes.Node]: |
110 | 148 | target_dir = out_dir / src_dir.name |
111 | 149 |
|
112 | 150 | extra_options = {"project": project} |
113 | | - if id: |
114 | | - extra_options["id"] = id |
| 151 | + |
| 152 | + _check_id(self.env.config, id, [title, project], self.options, extra_options) |
| 153 | + |
115 | 154 | source_files = self.get_src_files(self.options, src_dir, src_discover_config) |
116 | 155 |
|
117 | 156 | # add source files into the dependency |
|
0 commit comments