From f260b7a89049290331a45a252a47f6f8173a9029 Mon Sep 17 00:00:00 2001 From: Isaiah Akorita Date: Wed, 29 Jul 2026 00:03:09 +0100 Subject: [PATCH 01/13] add config file for llm.txt --- scripts/llms_config.py | 75 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 scripts/llms_config.py diff --git a/scripts/llms_config.py b/scripts/llms_config.py new file mode 100644 index 000000000..78f1d24cb --- /dev/null +++ b/scripts/llms_config.py @@ -0,0 +1,75 @@ +"""Config for building hvPlot markdown docs and llms.txt +from the nbsite llms builder. +""" + +from __future__ import annotations + +from pathlib import Path + +from nbsite.scripts import LlmsBuildConfig, LlmsSection, MarkdownSource + +ROOT = Path(__file__).parent.parent +DOC_DIR = ROOT / 'doc' +BUILTDOCS_DIR = ROOT / 'builtdocs' +OUTPUT_DIR = BUILTDOCS_DIR / 'markdown' +MARKDOWN_BASE_URL = '/markdown' + + +def _section_label(path: Path) -> str: + if path.stem == 'index': + return 'home' if path.parent == Path('.') else path.parent.as_posix().replace('-', ' ') + return path.stem.replace('_', ' ') + + +def _api_label(path: Path) -> str: + name = path.stem + for prefix in ('hvplot.hvPlot.', 'hvplot.plotting.'): + if name.startswith(prefix): + name = name.removeprefix(prefix) + break + return name.replace('_', ' ') + + +CONFIG = LlmsBuildConfig( + project_title='hvPlot', + project_description=( + 'hvPlot is a high-level plotting API for the HoloViz ecosystem built on HoloViews. ' + 'This file points to the selected markdown documentation for code-writing utility.' + ), + markdown_root=OUTPUT_DIR, + llms_output_path=BUILTDOCS_DIR / 'llms.txt', + markdown_base_url=MARKDOWN_BASE_URL, + sources=(MarkdownSource(source_dir=DOC_DIR, output_dir=OUTPUT_DIR),), + sections=( + LlmsSection( + title='Home', + description='Top-level pages in the hvPlot docs tree.', + path_prefix=Path('.'), + label_builder=_section_label, + path_filter=lambda path: len(path.parts) == 1, + ), + LlmsSection( + title='Tutorials', + description='Step-by-step guides to help you master hvPlot and the HoloViz ecosystem.', + path_prefix=Path('tutorials'), + ), + LlmsSection( + title='Gallery', + description='Example visualizations using hvPlot with different backends and datasets', + path_prefix=Path('gallery'), + ), + LlmsSection( + title='Reference', + description="API reference pages that provide detailed info about hvPlot's usage.", + path_prefix=Path('ref'), + label_builder=_section_label, + path_filter=lambda path: not path.is_relative_to(Path('ref/api/manual')), + ), + LlmsSection( + title='API', + description='hvPlot plotting APIs.', + path_prefix=Path('ref/api/manual'), + label_builder=_api_label, + ), + ), +) From 47240eec538554cc333b9a38f5b00319fe12f840 Mon Sep 17 00:00:00 2001 From: Isaiah Akorita Date: Wed, 29 Jul 2026 00:33:21 +0100 Subject: [PATCH 02/13] add newline --- scripts/llms_config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/llms_config.py b/scripts/llms_config.py index 78f1d24cb..6c4fb01df 100644 --- a/scripts/llms_config.py +++ b/scripts/llms_config.py @@ -33,7 +33,7 @@ def _api_label(path: Path) -> str: CONFIG = LlmsBuildConfig( project_title='hvPlot', project_description=( - 'hvPlot is a high-level plotting API for the HoloViz ecosystem built on HoloViews. ' + 'hvPlot is a high-level plotting API for the HoloViz ecosystem built on HoloViews. \n' 'This file points to the selected markdown documentation for code-writing utility.' ), markdown_root=OUTPUT_DIR, From 959a05ee6ae801d5fd587d74f2618463bec318ef Mon Sep 17 00:00:00 2001 From: Isaiah Akorita Date: Wed, 29 Jul 2026 11:02:18 +0100 Subject: [PATCH 03/13] add repo excludes --- scripts/llms_config.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/scripts/llms_config.py b/scripts/llms_config.py index 6c4fb01df..424e5dbaa 100644 --- a/scripts/llms_config.py +++ b/scripts/llms_config.py @@ -12,6 +12,7 @@ DOC_DIR = ROOT / 'doc' BUILTDOCS_DIR = ROOT / 'builtdocs' OUTPUT_DIR = BUILTDOCS_DIR / 'markdown' +REPO_EXCLUDES = ('.ipynb_checkpoints', 'user_guide') MARKDOWN_BASE_URL = '/markdown' @@ -39,7 +40,13 @@ def _api_label(path: Path) -> str: markdown_root=OUTPUT_DIR, llms_output_path=BUILTDOCS_DIR / 'llms.txt', markdown_base_url=MARKDOWN_BASE_URL, - sources=(MarkdownSource(source_dir=DOC_DIR, output_dir=OUTPUT_DIR),), + sources=( + MarkdownSource( + source_dir=DOC_DIR, + output_dir=OUTPUT_DIR, + exclude_dir_names=REPO_EXCLUDES, + ), + ), sections=( LlmsSection( title='Home', From b3c7b616163c88d8d2cd54c94412364bd3e9df29 Mon Sep 17 00:00:00 2001 From: Isaiah Akorita Date: Fri, 31 Jul 2026 01:37:39 +0100 Subject: [PATCH 04/13] add pixi command --- pixi.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pixi.toml b/pixi.toml index e6cc65663..9fb4495e3 100644 --- a/pixi.toml +++ b/pixi.toml @@ -245,9 +245,10 @@ HVPLOT_PATCH_PLOT_DOCSTRING_SIGNATURE = "false" [feature.doc.tasks] docs-build-sphinx = 'sphinx-build -j auto -b html doc builtdocs' _docs-install = 'python -m pip install --no-deps --disable-pip-version-check -e .' +_docs_markdown = 'python -m nbsite build-llms --config scripts/llms_config.py' # Depends on _docs-install instead of install as install # in the default environment -docs-build = { depends-on = ["_docs-install", "docs-build-sphinx"] } +docs-build = { depends-on = ["_docs-install", "docs-build-sphinx", "_docs_markdown"] } docs-server = 'python -m http.server 5500 --directory ./builtdocs' # ================== BUILD ==================== From 791ff95ab889e833a7137ed213a36f7bbab70912 Mon Sep 17 00:00:00 2001 From: Isaiah Akorita Date: Fri, 31 Jul 2026 01:45:52 +0100 Subject: [PATCH 05/13] update llmsection content --- scripts/llms_config.py | 93 +++++++++++++++++++++++++++++------------- 1 file changed, 64 insertions(+), 29 deletions(-) diff --git a/scripts/llms_config.py b/scripts/llms_config.py index 424e5dbaa..d4aa766c3 100644 --- a/scripts/llms_config.py +++ b/scripts/llms_config.py @@ -1,6 +1,4 @@ -"""Config for building hvPlot markdown docs and llms.txt -from the nbsite llms builder. -""" +"""Config for building hvPlot markdown docs and llms.txt.""" from __future__ import annotations @@ -12,11 +10,46 @@ DOC_DIR = ROOT / 'doc' BUILTDOCS_DIR = ROOT / 'builtdocs' OUTPUT_DIR = BUILTDOCS_DIR / 'markdown' -REPO_EXCLUDES = ('.ipynb_checkpoints', 'user_guide') MARKDOWN_BASE_URL = '/markdown' +PAGES = { + Path('index.md'), + Path('about.md'), + Path('developer_guide.md'), + Path('releases.md'), + Path('roadmap.md'), + Path('ref/index.md'), + Path('ref/deprecations.md'), + Path('ref/installation.md'), + Path('ref/api/index.md'), + Path('ref/api_compatibility/pandas/index.md'), + Path('ref/plotting_options/index.md'), + Path('ref/plotting_options/axis.md'), + Path('ref/plotting_options/data.md'), + Path('ref/plotting_options/geographic.md'), + Path('ref/plotting_options/interactivity.md'), + Path('ref/plotting_options/legend.md'), + Path('ref/plotting_options/size_layout.md'), + Path('ref/plotting_options/styling.md'), + Path('ref/api/manual/hvplot.hvPlot.area.md'), + Path('ref/api/manual/hvplot.hvPlot.bar.md'), + Path('ref/api/manual/hvplot.hvPlot.explorer.md'), + Path('ref/api/manual/hvplot.hvPlot.heatmap.md'), + Path('ref/api/manual/hvplot.hvPlot.line.md'), + Path('ref/api/manual/hvplot.hvPlot.points.md'), + Path('ref/api/manual/hvplot.hvPlot.scatter.md'), +} -def _section_label(path: Path) -> str: +ROOT_PAGES = { + Path('index.md'), + Path('about.md'), + Path('developer_guide.md'), + Path('releases.md'), + Path('roadmap.md'), +} + + +def _label(path: Path) -> str: if path.stem == 'index': return 'home' if path.parent == Path('.') else path.parent.as_posix().replace('-', ' ') return path.stem.replace('_', ' ') @@ -24,7 +57,7 @@ def _section_label(path: Path) -> str: def _api_label(path: Path) -> str: name = path.stem - for prefix in ('hvplot.hvPlot.', 'hvplot.plotting.'): + for prefix in ('hvplot.hvPlot.', 'hvplot.plotting.', 'hvplot.ui.', 'hvplot.networkx.'): if name.startswith(prefix): name = name.removeprefix(prefix) break @@ -33,10 +66,7 @@ def _api_label(path: Path) -> str: CONFIG = LlmsBuildConfig( project_title='hvPlot', - project_description=( - 'hvPlot is a high-level plotting API for the HoloViz ecosystem built on HoloViews. \n' - 'This file points to the selected markdown documentation for code-writing utility.' - ), + project_description='hvPlot documentation selected for LLM-friendly browsing.', markdown_root=OUTPUT_DIR, llms_output_path=BUILTDOCS_DIR / 'llms.txt', markdown_base_url=MARKDOWN_BASE_URL, @@ -44,39 +74,44 @@ def _api_label(path: Path) -> str: MarkdownSource( source_dir=DOC_DIR, output_dir=OUTPUT_DIR, - exclude_dir_names=REPO_EXCLUDES, + exclude_dir_names=('.ipynb_checkpoints', 'user_guide'), ), ), sections=( LlmsSection( title='Home', - description='Top-level pages in the hvPlot docs tree.', + description='Top-level pages and project overview.', path_prefix=Path('.'), - label_builder=_section_label, - path_filter=lambda path: len(path.parts) == 1, - ), - LlmsSection( - title='Tutorials', - description='Step-by-step guides to help you master hvPlot and the HoloViz ecosystem.', - path_prefix=Path('tutorials'), - ), - LlmsSection( - title='Gallery', - description='Example visualizations using hvPlot with different backends and datasets', - path_prefix=Path('gallery'), + path_filter=lambda path: path in ROOT_PAGES, + label_builder=_label, ), LlmsSection( title='Reference', - description="API reference pages that provide detailed info about hvPlot's usage.", + description='Installation notes and reference overview pages.', path_prefix=Path('ref'), - label_builder=_section_label, - path_filter=lambda path: not path.is_relative_to(Path('ref/api/manual')), + path_filter=lambda path: path in PAGES, + label_builder=_api_label, + ), + LlmsSection( + title='Plotting Options', + description='Core plotting configuration topics.', + path_prefix=Path('ref/plotting_options'), + path_filter=lambda path: path in PAGES, + label_builder=_label, ), LlmsSection( - title='API', - description='hvPlot plotting APIs.', + title='API Manual', + description='Representative hvPlot API examples.', path_prefix=Path('ref/api/manual'), + path_filter=lambda path: path in PAGES, label_builder=_api_label, ), + LlmsSection( + title='API Compatibility', + description='Compatibility notes for external plotting backends.', + path_prefix=Path('ref/api_compatibility'), + path_filter=lambda path: path in PAGES, + label_builder=_label, + ), ), ) From a69ddaf1c70b8e8621fd031db2541d591879bb3c Mon Sep 17 00:00:00 2001 From: Isaiah Akorita Date: Fri, 31 Jul 2026 13:13:52 +0100 Subject: [PATCH 06/13] fix index labelling --- scripts/llms_config.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/scripts/llms_config.py b/scripts/llms_config.py index d4aa766c3..c4397fc4a 100644 --- a/scripts/llms_config.py +++ b/scripts/llms_config.py @@ -49,13 +49,19 @@ } +def _index_label(path: Path) -> str: + return 'home' if path.parent == Path('.') else path.parent.as_posix().replace('-', ' ') + + def _label(path: Path) -> str: if path.stem == 'index': - return 'home' if path.parent == Path('.') else path.parent.as_posix().replace('-', ' ') + return _index_label(path) return path.stem.replace('_', ' ') def _api_label(path: Path) -> str: + if path.stem == 'index': + return _index_label(path) name = path.stem for prefix in ('hvplot.hvPlot.', 'hvplot.plotting.', 'hvplot.ui.', 'hvplot.networkx.'): if name.startswith(prefix): From f7f973eff2ba2b0f5d12c66a97508090a5dd2d72 Mon Sep 17 00:00:00 2001 From: Isaiah Akorita Date: Fri, 31 Jul 2026 13:14:08 +0100 Subject: [PATCH 07/13] trim sparse section --- scripts/llms_config.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/scripts/llms_config.py b/scripts/llms_config.py index c4397fc4a..4cbb0f545 100644 --- a/scripts/llms_config.py +++ b/scripts/llms_config.py @@ -112,12 +112,5 @@ def _api_label(path: Path) -> str: path_filter=lambda path: path in PAGES, label_builder=_api_label, ), - LlmsSection( - title='API Compatibility', - description='Compatibility notes for external plotting backends.', - path_prefix=Path('ref/api_compatibility'), - path_filter=lambda path: path in PAGES, - label_builder=_label, - ), ), ) From 061e2298ea5a2d445ada566811494f3a8c0f220f Mon Sep 17 00:00:00 2001 From: Isaiah Akorita Date: Fri, 31 Jul 2026 13:48:30 +0100 Subject: [PATCH 08/13] add test --- hvplot/tests/scripts/test_llms_config.py | 62 ++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 hvplot/tests/scripts/test_llms_config.py diff --git a/hvplot/tests/scripts/test_llms_config.py b/hvplot/tests/scripts/test_llms_config.py new file mode 100644 index 000000000..f8e113ebd --- /dev/null +++ b/hvplot/tests/scripts/test_llms_config.py @@ -0,0 +1,62 @@ +"""Tests for the nbsite LLM documentation build config (scripts/llms_config.py). + +The ``llms_config`` module requires ``nbsite``, which is only a doc +dependency, so the tests are skipped when it is not installed. +""" + +import sys +from pathlib import Path + +import pytest + +pytest.importorskip('nbsite') + +REPO_ROOT = Path(__file__).parents[3] +sys.path.insert(0, str(REPO_ROOT / 'scripts')) + +import llms_config # noqa: E402 +from nbsite.scripts import LlmsBuildConfig # noqa: E402 + +SOURCE_SUFFIXES = ('.md', '.ipynb', '.rst') + + +def _test_paths() -> set[Path]: + return llms_config.PAGES | llms_config.ROOT_PAGES + + +def _resolve_doc_path(path: Path) -> Path | None: + """Return the source file in ``doc/`` a configured page maps to, if any.""" + stem = path.with_suffix('') + for suffix in SOURCE_SUFFIXES: + candidate = llms_config.DOC_DIR / f'{stem}{suffix}' + if candidate.exists(): + return candidate + return None + + +def _matches(section, path: Path) -> bool: + prefix = section.path_prefix + matches_prefix = prefix in {Path(), Path('.')} or path.is_relative_to(prefix) + return matches_prefix and section.path_filter(path) + + +def test_config_is_valid() -> None: + assert isinstance(llms_config.CONFIG, LlmsBuildConfig) + + +@pytest.mark.parametrize('path', sorted(_test_paths())) +def test_referenced_pages_exist(path: Path) -> None: + assert _resolve_doc_path(path) is not None + + +def test_sections_match_at_least_one_page() -> None: + for section in llms_config.CONFIG.sections: + assert any(_matches(section, path) for path in _test_paths()) + + +@pytest.mark.parametrize('section', llms_config.CONFIG.sections, ids=lambda s: s.title) +def test_section_labels_are_unique_and_non_empty(section) -> None: + paths = [path for path in _test_paths() if _matches(section, path)] + labels = [section.label_builder(path) for path in paths] + assert all(labels) + assert len(labels) == len(set(labels)) From 68b41c1b3bf1a9effdb007ed43c187aba63d20df Mon Sep 17 00:00:00 2001 From: Isaiah Akorita Date: Tue, 4 Aug 2026 21:13:36 +0100 Subject: [PATCH 09/13] refactor --- scripts/llms_config.py | 130 +++++++++++++++++++++-------------------- 1 file changed, 66 insertions(+), 64 deletions(-) diff --git a/scripts/llms_config.py b/scripts/llms_config.py index 4cbb0f545..2c76ffdd1 100644 --- a/scripts/llms_config.py +++ b/scripts/llms_config.py @@ -1,7 +1,5 @@ """Config for building hvPlot markdown docs and llms.txt.""" -from __future__ import annotations - from pathlib import Path from nbsite.scripts import LlmsBuildConfig, LlmsSection, MarkdownSource @@ -10,107 +8,111 @@ DOC_DIR = ROOT / 'doc' BUILTDOCS_DIR = ROOT / 'builtdocs' OUTPUT_DIR = BUILTDOCS_DIR / 'markdown' -MARKDOWN_BASE_URL = '/markdown' -PAGES = { - Path('index.md'), - Path('about.md'), - Path('developer_guide.md'), +# Files that carry no LLM code-gen value and should be excluded from the build. +EXCLUDE_FILES = ( Path('releases.md'), Path('roadmap.md'), + Path('about.md'), + Path('developer_guide.md'), + # Nav-index pages that are pure toctree listings with no inline content. + Path('tutorials/index.md'), Path('ref/index.md'), - Path('ref/deprecations.md'), - Path('ref/installation.md'), - Path('ref/api/index.md'), - Path('ref/api_compatibility/pandas/index.md'), - Path('ref/plotting_options/index.md'), - Path('ref/plotting_options/axis.md'), - Path('ref/plotting_options/data.md'), - Path('ref/plotting_options/geographic.md'), - Path('ref/plotting_options/interactivity.md'), - Path('ref/plotting_options/legend.md'), - Path('ref/plotting_options/size_layout.md'), - Path('ref/plotting_options/styling.md'), - Path('ref/api/manual/hvplot.hvPlot.area.md'), - Path('ref/api/manual/hvplot.hvPlot.bar.md'), - Path('ref/api/manual/hvplot.hvPlot.explorer.md'), - Path('ref/api/manual/hvplot.hvPlot.heatmap.md'), - Path('ref/api/manual/hvplot.hvPlot.line.md'), - Path('ref/api/manual/hvplot.hvPlot.points.md'), - Path('ref/api/manual/hvplot.hvPlot.scatter.md'), + Path('ref/api_compatibility/index.md'), + Path('gallery/index.rst'), +) + +GETTING_STARTED = { + Path('tutorials/getting_started.md'): 'Installing hvPlot and creating your first plots', + Path('tutorials/getting_started_pandas.md'): 'Using hvPlot with a Pandas DataFrame', + Path('ref/installation.md'): 'Installing hvPlot', } -ROOT_PAGES = { - Path('index.md'), - Path('about.md'), - Path('developer_guide.md'), - Path('releases.md'), - Path('roadmap.md'), +REFERENCE = { + Path('ref/api/index.md'): 'Overview of the hvPlot API', + Path('ref/api_compatibility/pandas/Pandas_API.md'): 'Pandas vs hvPlot API comparison', + Path('ref/data_libraries.md'): 'Working with data libraries supported by hvPlot', + Path('ref/plotting_extensions.md'): 'Plotting backends and extensions', + Path('ref/plotting_options/index.md'): 'Options to control appearance and behavior of plots', + Path('ref/deprecations.md'): 'Deprecated features and migration notes', } -def _index_label(path: Path) -> str: - return 'home' if path.parent == Path('.') else path.parent.as_posix().replace('-', ' ') +def _label(path: Path) -> str: + """Title-cased label, using parent dir name for index pages.""" + stem = path.parent.name if path.stem == 'index' else path.stem + return stem.replace('_', ' ').replace('-', ' ').title() -def _label(path: Path) -> str: - if path.stem == 'index': - return _index_label(path) - return path.stem.replace('_', ' ') +def dict_filter(mapping: dict[Path, str]): + return lambda path: path in mapping -def _api_label(path: Path) -> str: - if path.stem == 'index': - return _index_label(path) - name = path.stem - for prefix in ('hvplot.hvPlot.', 'hvplot.plotting.', 'hvplot.ui.', 'hvplot.networkx.'): - if name.startswith(prefix): - name = name.removeprefix(prefix) - break - return name.replace('_', ' ') +def dict_description(mapping: dict[Path, str]): + return lambda path: mapping[path] CONFIG = LlmsBuildConfig( project_title='hvPlot', - project_description='hvPlot documentation selected for LLM-friendly browsing.', + project_description=( + 'hvPlot is a high-level and interactive Pandas-like .plot() API built on ' + 'HoloViews,\nletting you visualize data from Pandas, Xarray, GeoPandas, Dask, ' + 'Polars, and DuckDB with a familiar interface.\n' + 'This file lists the most important documentation pages for LLM-assisted ' + 'development; not all generated doc links are shown.' + ), markdown_root=OUTPUT_DIR, llms_output_path=BUILTDOCS_DIR / 'llms.txt', - markdown_base_url=MARKDOWN_BASE_URL, + markdown_base_url='/markdown', sources=( MarkdownSource( source_dir=DOC_DIR, output_dir=OUTPUT_DIR, - exclude_dir_names=('.ipynb_checkpoints', 'user_guide'), + rendered_source_dir=BUILTDOCS_DIR, + exclude_dir_names=('.ipynb_checkpoints', 'user_guide', 'governance'), + exclude_files=EXCLUDE_FILES, ), ), sections=( LlmsSection( - title='Home', - description='Top-level pages and project overview.', + title='getting started', + description='Guides for installing hvPlot and creating your first plots', path_prefix=Path('.'), - path_filter=lambda path: path in ROOT_PAGES, + path_filter=dict_filter(GETTING_STARTED), label_builder=_label, + description_builder=dict_description(GETTING_STARTED), + group='Documentation', ), LlmsSection( - title='Reference', - description='Installation notes and reference overview pages.', + title='reference', + description='Overview, compatibility, and configuration pages', path_prefix=Path('ref'), - path_filter=lambda path: path in PAGES, - label_builder=_api_label, + path_filter=dict_filter(REFERENCE), + label_builder=_label, + description_builder=dict_description(REFERENCE), + group='Documentation', + ), + LlmsSection( + title='gallery', + description='Example plots organized by category. ' + 'Category index pages contain links to all examples in that category.', + path_prefix=Path('gallery'), + path_filter=lambda p: p.stem != 'index', + url_pattern='/markdown/gallery/{path}.md', + group='Documentation', ), LlmsSection( title='Plotting Options', - description='Core plotting configuration topics.', + description='Configuration options for controlling how data is styled and displayed.', path_prefix=Path('ref/plotting_options'), - path_filter=lambda path: path in PAGES, - label_builder=_label, + path_filter=lambda p: p.stem != 'index', + url_pattern='/markdown/ref/plotting_options/{stem}.md', ), LlmsSection( - title='API Manual', - description='Representative hvPlot API examples.', + title='API Reference', + description='Per-method reference pages with signatures, parameters, and examples.', path_prefix=Path('ref/api/manual'), - path_filter=lambda path: path in PAGES, - label_builder=_api_label, + url_pattern='/markdown/ref/api/manual/{stem}.md', ), ), ) From da8f5ed68e20ae1ed14ac465843bf6ae01ddad75 Mon Sep 17 00:00:00 2001 From: Isaiah Akorita Date: Tue, 4 Aug 2026 21:21:03 +0100 Subject: [PATCH 10/13] update tests --- hvplot/tests/scripts/test_llms_config.py | 49 ++++++++++++++---------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/hvplot/tests/scripts/test_llms_config.py b/hvplot/tests/scripts/test_llms_config.py index f8e113ebd..2735fefe8 100644 --- a/hvplot/tests/scripts/test_llms_config.py +++ b/hvplot/tests/scripts/test_llms_config.py @@ -20,18 +20,20 @@ SOURCE_SUFFIXES = ('.md', '.ipynb', '.rst') -def _test_paths() -> set[Path]: - return llms_config.PAGES | llms_config.ROOT_PAGES - - -def _resolve_doc_path(path: Path) -> Path | None: - """Return the source file in ``doc/`` a configured page maps to, if any.""" - stem = path.with_suffix('') - for suffix in SOURCE_SUFFIXES: - candidate = llms_config.DOC_DIR / f'{stem}{suffix}' - if candidate.exists(): - return candidate - return None +def _all_doc_paths() -> set[Path]: + """Every markdown path the sources produce, relative to the markdown root.""" + paths: set[Path] = set() + for source in llms_config.CONFIG.sources: + for path in source.source_dir.rglob('*'): + if not path.is_file(): + continue + rel_path = path.relative_to(source.source_dir) + if any(part in source.exclude_dir_names for part in rel_path.parts): + continue + if rel_path.suffix not in source.include_suffixes: + continue + paths.add(rel_path.with_suffix('.md')) + return paths def _matches(section, path: Path) -> bool: @@ -40,23 +42,30 @@ def _matches(section, path: Path) -> bool: return matches_prefix and section.path_filter(path) -def test_config_is_valid() -> None: - assert isinstance(llms_config.CONFIG, LlmsBuildConfig) +def _section_paths(section) -> list[Path]: + return [path for path in sorted(_all_doc_paths()) if _matches(section, path)] -@pytest.mark.parametrize('path', sorted(_test_paths())) -def test_referenced_pages_exist(path: Path) -> None: - assert _resolve_doc_path(path) is not None +def test_config_is_valid() -> None: + assert isinstance(llms_config.CONFIG, LlmsBuildConfig) -def test_sections_match_at_least_one_page() -> None: +def test_every_section_matches_at_least_one_page() -> None: for section in llms_config.CONFIG.sections: - assert any(_matches(section, path) for path in _test_paths()) + assert _section_paths(section) @pytest.mark.parametrize('section', llms_config.CONFIG.sections, ids=lambda s: s.title) def test_section_labels_are_unique_and_non_empty(section) -> None: - paths = [path for path in _test_paths() if _matches(section, path)] + paths = _section_paths(section) labels = [section.label_builder(path) for path in paths] assert all(labels) assert len(labels) == len(set(labels)) + + +@pytest.mark.parametrize('section', llms_config.CONFIG.sections, ids=lambda s: s.title) +def test_section_descriptions_are_non_empty(section) -> None: + assert section.description.strip() + if section.description_builder is not None: + for path in _section_paths(section): + assert section.description_builder(path).strip() From 439cfb2251a546c150ff4cb88f4a1e4580ac14f4 Mon Sep 17 00:00:00 2001 From: Isaiah Akorita Date: Tue, 4 Aug 2026 21:49:52 +0100 Subject: [PATCH 11/13] copilot review fix --- hvplot/tests/scripts/test_llms_config.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hvplot/tests/scripts/test_llms_config.py b/hvplot/tests/scripts/test_llms_config.py index 2735fefe8..4e5e14b80 100644 --- a/hvplot/tests/scripts/test_llms_config.py +++ b/hvplot/tests/scripts/test_llms_config.py @@ -17,8 +17,6 @@ import llms_config # noqa: E402 from nbsite.scripts import LlmsBuildConfig # noqa: E402 -SOURCE_SUFFIXES = ('.md', '.ipynb', '.rst') - def _all_doc_paths() -> set[Path]: """Every markdown path the sources produce, relative to the markdown root.""" @@ -30,6 +28,8 @@ def _all_doc_paths() -> set[Path]: rel_path = path.relative_to(source.source_dir) if any(part in source.exclude_dir_names for part in rel_path.parts): continue + if rel_path in source.exclude_files: + continue if rel_path.suffix not in source.include_suffixes: continue paths.add(rel_path.with_suffix('.md')) From f94ae069442681fd0f0a0d1039701207b61c181c Mon Sep 17 00:00:00 2001 From: Isaiah Akorita Date: Thu, 6 Aug 2026 15:55:17 +0100 Subject: [PATCH 12/13] remove test dir --- hvplot/tests/scripts/test_llms_config.py | 71 ------------------------ 1 file changed, 71 deletions(-) delete mode 100644 hvplot/tests/scripts/test_llms_config.py diff --git a/hvplot/tests/scripts/test_llms_config.py b/hvplot/tests/scripts/test_llms_config.py deleted file mode 100644 index 4e5e14b80..000000000 --- a/hvplot/tests/scripts/test_llms_config.py +++ /dev/null @@ -1,71 +0,0 @@ -"""Tests for the nbsite LLM documentation build config (scripts/llms_config.py). - -The ``llms_config`` module requires ``nbsite``, which is only a doc -dependency, so the tests are skipped when it is not installed. -""" - -import sys -from pathlib import Path - -import pytest - -pytest.importorskip('nbsite') - -REPO_ROOT = Path(__file__).parents[3] -sys.path.insert(0, str(REPO_ROOT / 'scripts')) - -import llms_config # noqa: E402 -from nbsite.scripts import LlmsBuildConfig # noqa: E402 - - -def _all_doc_paths() -> set[Path]: - """Every markdown path the sources produce, relative to the markdown root.""" - paths: set[Path] = set() - for source in llms_config.CONFIG.sources: - for path in source.source_dir.rglob('*'): - if not path.is_file(): - continue - rel_path = path.relative_to(source.source_dir) - if any(part in source.exclude_dir_names for part in rel_path.parts): - continue - if rel_path in source.exclude_files: - continue - if rel_path.suffix not in source.include_suffixes: - continue - paths.add(rel_path.with_suffix('.md')) - return paths - - -def _matches(section, path: Path) -> bool: - prefix = section.path_prefix - matches_prefix = prefix in {Path(), Path('.')} or path.is_relative_to(prefix) - return matches_prefix and section.path_filter(path) - - -def _section_paths(section) -> list[Path]: - return [path for path in sorted(_all_doc_paths()) if _matches(section, path)] - - -def test_config_is_valid() -> None: - assert isinstance(llms_config.CONFIG, LlmsBuildConfig) - - -def test_every_section_matches_at_least_one_page() -> None: - for section in llms_config.CONFIG.sections: - assert _section_paths(section) - - -@pytest.mark.parametrize('section', llms_config.CONFIG.sections, ids=lambda s: s.title) -def test_section_labels_are_unique_and_non_empty(section) -> None: - paths = _section_paths(section) - labels = [section.label_builder(path) for path in paths] - assert all(labels) - assert len(labels) == len(set(labels)) - - -@pytest.mark.parametrize('section', llms_config.CONFIG.sections, ids=lambda s: s.title) -def test_section_descriptions_are_non_empty(section) -> None: - assert section.description.strip() - if section.description_builder is not None: - for path in _section_paths(section): - assert section.description_builder(path).strip() From cf51cd3d7046701f9e1f037bac7a81f395b7dc7f Mon Sep 17 00:00:00 2001 From: Isaiah Akorita Date: Thu, 6 Aug 2026 15:55:35 +0100 Subject: [PATCH 13/13] bump nbsite version --- pixi.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pixi.toml b/pixi.toml index 9fb4495e3..75afc5298 100644 --- a/pixi.toml +++ b/pixi.toml @@ -231,7 +231,7 @@ channels = [ "conda-forge" ] [feature.doc.dependencies] -nbsite = ">=0.9.0a12" +nbsite = ">=0.10.0a0" sphinxext-rediraffe = "*" numpydoc = "*" sphinxcontrib-mermaid = "*"