Improve _sources directory handling (#47)

This commit is contained in:
Jared Dillard
2025-10-13 23:53:46 -07:00
committed by GitHub
parent c45ebb0369
commit fc03bdb47e
4 changed files with 160 additions and 12 deletions
+6
View File
@@ -1,6 +1,12 @@
Changelog Changelog
========= =========
0.6.0
-----
- Improve _sources directory handling
`#47 <https://github.com/jdillard/sphinx-llms-txt/pull/47>`_
0.5.3 0.5.3
----- -----
+1 -1
View File
@@ -21,7 +21,7 @@ from .manager import LLMSFullManager
from .processor import DocumentProcessor from .processor import DocumentProcessor
from .writer import FileWriter from .writer import FileWriter
__version__ = "0.5.3" __version__ = "0.6.0"
# Export classes needed by tests # Export classes needed by tests
__all__ = [ __all__ = [
+28 -11
View File
@@ -204,25 +204,42 @@ class LLMSFullManager:
sources_dir = path sources_dir = path
break break
if not sources_dir: # Get the correct page order (with or without source suffixes)
logger.warning(
"Could not find _sources directory, skipping llms-full creation"
)
return
# Get the correct page order with source suffixes
page_order = self.collector.get_page_order(sources_dir) page_order = self.collector.get_page_order(sources_dir)
if not page_order: if not page_order:
logger.warning( logger.warning("Could not determine page order, skipping file generation")
"Could not determine page order, skipping llms-full creation"
)
return return
# Apply exclusion filter if configured # Apply exclusion filter if configured
page_order = self.collector.filter_excluded_pages(page_order) page_order = self.collector.filter_excluded_pages(page_order)
# Determine output file name and location # If no sources directory, only generate llms.txt and return early
if not sources_dir:
# Generate llms.txt if requested
if self.config.get("llms_txt_file"):
filtered_page_order = self._filter_ignored_pages(page_order)
self.writer.write_verbose_info_to_file(
filtered_page_order,
self.collector.page_titles,
0, # No line count since no llms-full.txt
)
# Only warn if user explicitly wants llms-full.txt
if self.config.get("llms_txt_full_file"):
# Check if html_copy_source is False
if self.app and not self.app.config.html_copy_source:
logger.warning(
"Could not find _sources directory, skipping llms-full.txt."
"Set html_copy_source = True in conf.py to enable."
)
else:
logger.warning(
"Could not find _sources directory, skipping llms-full.txt"
)
return
# Determine output file name and location for llms-full.txt
output_filename = self.config.get("llms_txt_full_filename") output_filename = self.config.get("llms_txt_full_filename")
output_path = Path(outdir) / output_filename output_path = Path(outdir) / output_filename
+125
View File
@@ -1031,3 +1031,128 @@ def test_code_files_ignored_patterns(tmp_path, caplog):
assert ( assert (
"Code file pattern 'docs/**/*.rst' ignored." in captured_warnings[0] "Code file pattern 'docs/**/*.rst' ignored." in captured_warnings[0]
), f"Warning message should contain expected text. Got: {captured_warnings[0]}" ), f"Warning message should contain expected text. Got: {captured_warnings[0]}"
def test_llms_txt_generated_without_sources_dir(tmp_path):
"""Test that llms.txt is generated even when _sources directory doesn't exist."""
from sphinx_llms_txt.manager import LLMSFullManager
# Create manager
manager = LLMSFullManager()
# Set config to enable llms.txt
config = {
"llms_txt_file": True,
"llms_txt_filename": "llms.txt",
"llms_txt_full_file": True,
"llms_txt_full_filename": "llms-full.txt",
"llms_txt_exclude": [],
"llms_txt_directives": [],
}
manager.set_config(config)
# Create directories (but no _sources)
outdir = tmp_path / "build"
srcdir = tmp_path / "source"
outdir.mkdir()
srcdir.mkdir()
# Mock env with documents
class MockEnv:
all_docs = {"index": None, "about": None}
titles = {
"index": type("TitleNode", (), {"astext": lambda self: "Home"})(),
"about": type("TitleNode", (), {"astext": lambda self: "About"})(),
}
toctree_includes = {"index": ["about"]}
manager.set_env(MockEnv())
manager.set_master_doc("index")
# Update page titles directly in the collector
manager.update_page_title("index", "Home")
manager.update_page_title("about", "About")
# Call combine_sources - should generate llms.txt even without _sources
manager.combine_sources(str(outdir), str(srcdir))
# Verify llms.txt was created
llms_txt = outdir / "llms.txt"
assert llms_txt.exists(), "llms.txt should be generated even without _sources"
# Verify llms-full.txt was NOT created (since no _sources)
llms_full_txt = outdir / "llms-full.txt"
assert (
not llms_full_txt.exists()
), "llms-full.txt should not be generated without _sources"
# Read llms.txt and verify it has content
with open(llms_txt, "r", encoding="utf-8") as f:
content = f.read()
# Should contain page titles and links
assert "Home" in content
assert "About" in content
assert "index.html" in content
assert "about.html" in content
def test_llms_txt_no_warning_when_full_file_disabled(tmp_path, caplog):
"""
Test that no warning is logged when llms_txt_full_file=False and
_sources doesn't exist.
"""
from unittest.mock import patch
from sphinx_llms_txt.manager import LLMSFullManager
# Create manager
manager = LLMSFullManager()
# Set config with llms_txt_full_file=False
config = {
"llms_txt_file": True,
"llms_txt_filename": "llms.txt",
"llms_txt_full_file": False, # User doesn't want llms-full.txt
"llms_txt_full_filename": "llms-full.txt",
"llms_txt_exclude": [],
"llms_txt_directives": [],
}
manager.set_config(config)
# Create directories (but no _sources)
outdir = tmp_path / "build"
srcdir = tmp_path / "source"
outdir.mkdir()
srcdir.mkdir()
# Mock env with documents
class MockEnv:
all_docs = {"index": None}
titles = {"index": type("TitleNode", (), {"astext": lambda self: "Home"})()}
toctree_includes = {"index": []}
manager.set_env(MockEnv())
manager.set_master_doc("index")
manager.update_page_title("index", "Home")
# Capture warnings
captured_warnings = []
def capture_warning(message, *args, **kwargs):
if "_sources" in str(message):
captured_warnings.append(message)
with patch("sphinx_llms_txt.manager.logger.warning", side_effect=capture_warning):
# Call combine_sources
manager.combine_sources(str(outdir), str(srcdir))
# Verify NO warning was logged since llms_txt_full_file=False
assert (
len(captured_warnings) == 0
), "No warning should be logged when llms_txt_full_file=False"
# Verify llms.txt was still created
llms_txt = outdir / "llms.txt"
assert llms_txt.exists()