-
Notifications
You must be signed in to change notification settings - Fork 22
Any folder in docs #431
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Any folder in docs #431
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| Use Any Folder for Documentation | ||
| ============================================= | ||
|
|
||
| Generally, your documentation must be ``docs/``, | ||
| but the RST files for a module may live closer to the code they describe, | ||
| for example in ``src/my_module/docs/``. | ||
| You can symlink the folders by adding to your ``conf.py``: | ||
|
|
||
| .. code-block:: python | ||
| score_any_folder_mapping = { | ||
| "../score/containers/docs": "component/containers", | ||
| } | ||
| With this configuration, all files in ``score/containers/docs/`` become available at ``docs/component/containers/``. | ||
|
|
||
| If you have ``docs/component/overview.rst``, for example, | ||
| you can include the component documentation via ``toctree``: | ||
|
|
||
| .. code-block:: rst | ||
| .. toctree:: | ||
| containers/index | ||
| Only relative links are allowed. | ||
|
|
||
| The symlinks will show up in your sources. | ||
| **Don't commit the symlinks to git!** | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would it be possible to crate the symlinks with a special pattern e.g. I'm pretty sure that otherwise this would often be done incorrectly. One advantage of βfreeβ is that it's clear which parts are outside the docs folder. |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| Any Folder | ||
| ========== | ||
|
|
||
| The extension ``score_any_folder`` allows documentation roots to stay in ``docs/`` | ||
| while pulling in source files from anywhere else in the repository. | ||
|
|
||
| It does this by creating symlinks inside the Sphinx source directory (``confdir``) that point to the configured external directories. | ||
| Sphinx then discovers and buildsthose files as if they were part of ``docs/`` from the start. | ||
|
|
||
| The extension hooks into the ``builder-inited`` event, | ||
| which fires before Sphinx reads any documents. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -71,6 +71,15 @@ Hello there | |
| `ubCode <https://ubcode.useblocks.com>`__ VS Code extension. | ||
| Getting IDE support for Sphinx-Needs in a Bazel context made easy. | ||
|
|
||
| .. grid-item-card:: | ||
|
|
||
| Any Folder | ||
| ^^^ | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Enlarge to fit the headline |
||
| Learn about the :doc:`any_folder` extension that creates symlinks | ||
| from arbitrary repository locations into the docs folder, | ||
| allowing Sphinx to discover and build source files | ||
| that live outside the documentation root. | ||
|
|
||
|
|
||
|
|
||
| .. toctree:: | ||
|
|
@@ -83,3 +92,4 @@ Hello there | |
| Source Code Linker <source_code_linker> | ||
| Extension Guide <extension_guide> | ||
| Sync TOML <sync_toml> | ||
| Any Folder <any_folder> | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| # ******************************************************************************* | ||
| # Copyright (c) 2026 Contributors to the Eclipse Foundation | ||
| # | ||
| # See the NOTICE file(s) distributed with this work for additional | ||
| # information regarding copyright ownership. | ||
| # | ||
| # This program and the accompanying materials are made available under the | ||
| # terms of the Apache License Version 2.0 which is available at | ||
| # https://www.apache.org/licenses/LICENSE-2.0 | ||
| # | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
| # ******************************************************************************* | ||
|
|
||
| load("@aspect_rules_py//py:defs.bzl", "py_library") | ||
| load("@docs_as_code_hub_env//:requirements.bzl", "requirement") | ||
| load("@score_tooling//:defs.bzl", "score_py_pytest") | ||
|
|
||
| filegroup( | ||
| name = "sources", | ||
| srcs = glob(["*.py"]), | ||
| ) | ||
|
|
||
| filegroup( | ||
| name = "tests", | ||
| srcs = glob(["tests/*.py"]), | ||
| ) | ||
|
|
||
| filegroup( | ||
| name = "all_sources", | ||
| srcs = [ | ||
| ":sources", | ||
| ":tests", | ||
| ], | ||
| visibility = ["//visibility:public"], | ||
| ) | ||
|
|
||
| py_library( | ||
| name = "score_any_folder", | ||
| srcs = [":sources"], | ||
| imports = ["."], | ||
| visibility = ["//visibility:public"], | ||
| deps = [requirement("sphinx")], | ||
| ) | ||
|
|
||
| score_py_pytest( | ||
| name = "score_any_folder_tests", | ||
| size = "small", | ||
| srcs = glob(["tests/*.py"]), | ||
| deps = [":score_any_folder"], | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,96 @@ | ||
| # ******************************************************************************* | ||
| # Copyright (c) 2026 Contributors to the Eclipse Foundation | ||
| # | ||
| # See the NOTICE file(s) distributed with this work for additional | ||
| # information regarding copyright ownership. | ||
| # | ||
| # This program and the accompanying materials are made available under the | ||
| # terms of the Apache License Version 2.0 which is available at | ||
| # https://www.apache.org/licenses/LICENSE-2.0 | ||
| # | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
| # ******************************************************************************* | ||
| """Sphinx extension that creates symlinks from arbitrary locations into the | ||
| documentation source directory, allowing sphinx-build to include source | ||
| files that live outside ``docs/``. | ||
| Configuration in ``conf.py``:: | ||
| score_any_folder_mapping = { | ||
| "../src/my_module/docs": "my_module", | ||
| } | ||
| Each entry is a ``source: target`` pair where: | ||
| * ``source`` β path to the directory to expose, relative to ``confdir`` | ||
| (the directory containing ``conf.py``). | ||
| * ``target`` β path of the symlink to create, relative to ``confdir``. | ||
| The extension creates the symlinks on ``builder-inited``, before Sphinx | ||
| starts reading any documents. Existing correct symlinks are left in place | ||
| (idempotent); a symlink pointing to the wrong target is replaced. A | ||
| Misconfigured pairs (absolute paths, non-symlink path at the target location) | ||
| are logged as errors and skipped. | ||
| """ | ||
| from pathlib import Path | ||
|
|
||
| from sphinx.application import Sphinx | ||
| from sphinx.util.logging import getLogger | ||
|
|
||
| logger = getLogger(__name__) | ||
|
|
||
|
|
||
| def setup(app: Sphinx) -> dict[str, str | bool]: | ||
| app.add_config_value("score_any_folder_mapping", default={}, rebuild="env") | ||
| app.connect("builder-inited", _create_symlinks) | ||
| return { | ||
| "version": "0.1", | ||
| "parallel_read_safe": True, | ||
| "parallel_write_safe": True, | ||
| } | ||
|
|
||
|
|
||
| def _symlink_pairs(app: Sphinx) -> list[tuple[Path, Path]]: | ||
| """Return ``(resolved_source, link_path)`` pairs from the mapping.""" | ||
| confdir = Path(app.confdir) | ||
| pairs = [] | ||
| for source_rel, target_rel in app.config.score_any_folder_mapping.items(): | ||
| if Path(source_rel).is_absolute(): | ||
| logger.error( | ||
| "score_any_folder: source path must be relative, got: %r; skipping", | ||
| source_rel, | ||
| ) | ||
| continue | ||
| if Path(target_rel).is_absolute(): | ||
| logger.error( | ||
| "score_any_folder: target path must be relative, got: %r; skipping", | ||
| target_rel, | ||
| ) | ||
| continue | ||
| source = (confdir / source_rel).resolve() | ||
| link = confdir / target_rel | ||
| pairs.append((source, link)) | ||
| return pairs | ||
|
|
||
|
|
||
| def _create_symlinks(app: Sphinx) -> None: | ||
| for source, link in _symlink_pairs(app): | ||
| if link.is_symlink(): | ||
| if link.resolve() == source: | ||
| logger.debug("score_any_folder: symlink already correct: %s", link) | ||
| continue | ||
| logger.info( | ||
| "score_any_folder: replacing stale symlink %s -> %s", link, source | ||
| ) | ||
| link.unlink() | ||
| elif link.exists(): | ||
| logger.error( | ||
| "score_any_folder: target path already exists and is not a symlink: " | ||
| "%s; skipping", | ||
| link, | ||
| ) | ||
| continue | ||
|
|
||
| link.parent.mkdir(parents=True, exist_ok=True) | ||
| link.symlink_to(source) | ||
| logger.debug("score_any_folder: created symlink %s -> %s", link, source) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| # ******************************************************************************* | ||
| # Copyright (c) 2026 Contributors to the Eclipse Foundation | ||
| # | ||
| # See the NOTICE file(s) distributed with this work for additional | ||
| # information regarding copyright ownership. | ||
| # | ||
| # This program and the accompanying materials are made available under the | ||
| # terms of the Apache License Version 2.0 which is available at | ||
| # https://www.apache.org/licenses/LICENSE-2.0 | ||
| # | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
| # ******************************************************************************* |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
reduce to fit the heading