2222
2323import posixpath
2424import re
25+ from dataclasses import dataclass , field
2526from pathlib import Path
2627
2728from mkdocs .config .defaults import MkDocsConfig
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
5259def _site_url (config : MkDocsConfig ) -> str :
@@ -60,21 +67,19 @@ def _md_uri(file: File) -> str:
6067
6168def 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
6975def 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
7681def 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
120125def _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
130135def 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