Skip to content

Commit 6af8e07

Browse files
committed
docs: hold llms_txt hook state in a dataclass instead of module globals
Same shape as the v1.x backport of this hook, whose ruff config bans global statements (PLW0603).
1 parent e41829f commit 6af8e07

1 file changed

Lines changed: 29 additions & 22 deletions

File tree

docs/hooks/llms_txt.py

Lines changed: 29 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
import posixpath
2424
import re
25+
from dataclasses import dataclass, field
2526
from pathlib import Path
2627

2728
from mkdocs.config.defaults import MkDocsConfig
@@ -43,10 +44,16 @@
4344
_SNIPPET_LINE = re.compile(r'^(?P<indent>[ \t]*)--8<-- "(?P<path>[^"\n]+)"$', flags=re.MULTILINE)
4445
_MD_LINK = re.compile(r'(\]\()([^)\s]+\.md)(#[^)\s]*)?( +"[^"]*")?(\))')
4546

46-
_page_markdown: dict[str, str] = {}
47-
_rendition_uris: set[str] = set()
48-
_nav: Navigation | None = None
49-
_files: Files | None = None
47+
48+
@dataclass
49+
class _State:
50+
page_markdown: dict[str, str] = field(default_factory=dict)
51+
rendition_uris: set[str] = field(default_factory=set)
52+
nav: Navigation | None = None
53+
files: Files | None = None
54+
55+
56+
_state = _State()
5057

5158

5259
def _site_url(config: MkDocsConfig) -> str:
@@ -60,21 +67,19 @@ def _md_uri(file: File) -> str:
6067

6168
def on_config(config: MkDocsConfig) -> None:
6269
# `mkdocs serve` rebuilds reuse the imported module; start each build clean.
63-
global _nav, _files
64-
_page_markdown.clear()
65-
_rendition_uris.clear()
66-
_nav = _files = None
70+
_state.page_markdown.clear()
71+
_state.rendition_uris.clear()
72+
_state.nav = _state.files = None
6773

6874

6975
def on_nav(nav: Navigation, config: MkDocsConfig, files: Files) -> None:
70-
global _nav, _files
71-
_nav = nav
72-
_files = files
73-
_rendition_uris.update(page.file.src_uri for page in nav.pages if not page.file.src_uri.startswith("api/"))
76+
_state.nav = nav
77+
_state.files = files
78+
_state.rendition_uris.update(page.file.src_uri for page in nav.pages if not page.file.src_uri.startswith("api/"))
7479

7580

7681
def on_page_markdown(markdown: str, page: Page, config: MkDocsConfig, files: Files) -> str | None:
77-
if page.file.src_uri not in _rendition_uris:
82+
if page.file.src_uri not in _state.rendition_uris:
7883
return None
7984

8085
# Same anchor as the pymdownx.snippets `base_path` in mkdocs.yml.
@@ -110,35 +115,37 @@ def rewrite(match: re.Match[str]) -> str:
110115
if linked is None:
111116
return match.group(0)
112117
# Pages without a markdown rendition (the api/ stubs) link to their HTML instead.
113-
url = _md_uri(linked) if linked.src_uri in _rendition_uris else linked.url
118+
url = _md_uri(linked) if linked.src_uri in _state.rendition_uris else linked.url
114119
return f"{opening}{site_url}{url}{anchor or ''}{title or ''}{closing}"
115120

116-
_page_markdown[page.file.src_uri] = _MD_LINK.sub(rewrite, resolved)
121+
_state.page_markdown[page.file.src_uri] = _MD_LINK.sub(rewrite, resolved)
117122
return None
118123

119124

120125
def _section_pages(section: Section) -> list[Page]:
121126
pages: list[Page] = []
122127
for child in section.children:
123-
if isinstance(child, Page) and child.file.src_uri in _rendition_uris:
128+
if isinstance(child, Page) and child.file.src_uri in _state.rendition_uris:
124129
pages.append(child)
125130
elif isinstance(child, Section):
126131
pages.extend(_section_pages(child))
127132
return pages
128133

129134

130135
def on_post_build(config: MkDocsConfig) -> None:
131-
assert _nav is not None and _files is not None
132-
missing = _rendition_uris - _page_markdown.keys()
136+
assert _state.nav is not None and _state.files is not None
137+
missing = _state.rendition_uris - _state.page_markdown.keys()
133138
if missing:
134139
raise PluginError(f"llms_txt: pages skipped this build (is this a --dirty build?): {sorted(missing)}")
135140

136141
site_dir = Path(config.site_dir)
137142
site_url = _site_url(config)
138143

139-
top_level = [item for item in _nav.items if isinstance(item, Page) and item.file.src_uri in _rendition_uris]
144+
top_level = [
145+
item for item in _state.nav.items if isinstance(item, Page) and item.file.src_uri in _state.rendition_uris
146+
]
140147
sections: list[tuple[str, list[Page]]] = [("Docs", top_level)] if top_level else []
141-
for item in _nav.items:
148+
for item in _state.nav.items:
142149
if isinstance(item, Section):
143150
pages = _section_pages(item)
144151
if pages:
@@ -149,7 +156,7 @@ def on_post_build(config: MkDocsConfig) -> None:
149156
for title, pages in sections:
150157
index += [f"## {title}", ""]
151158
for page in pages:
152-
markdown = _page_markdown[page.file.src_uri]
159+
markdown = _state.page_markdown[page.file.src_uri]
153160
(site_dir / _md_uri(page.file)).write_text(markdown, encoding="utf-8")
154161

155162
description = page.meta.get("description")
@@ -162,7 +169,7 @@ def on_post_build(config: MkDocsConfig) -> None:
162169

163170
index += ["## Optional", ""]
164171
for src_uri, title, description in _OPTIONAL_PAGES:
165-
linked = _files.get_file_from_path(src_uri)
172+
linked = _state.files.get_file_from_path(src_uri)
166173
if linked is None:
167174
raise PluginError(f"llms_txt: optional page {src_uri} not found")
168175
index.append(f"- [{title}]({site_url}{linked.url}): {description}")

0 commit comments

Comments
 (0)