Coverage for src/mkdocs_spellcheck/plugin.py: 0.00%
51 statements
« prev ^ index » next coverage.py v7.4.4, created at 2024-05-05 19:29 +0200
« prev ^ index » next coverage.py v7.4.4, created at 2024-05-05 19:29 +0200
1"""MkDocs SpellCheck package.
3A spell checker plugin for MkDocs.
4"""
6from __future__ import annotations
8from fnmatch import fnmatch
9from pathlib import Path
10from typing import TYPE_CHECKING, Any
12from mkdocs.config.config_options import Type as MkType
13from mkdocs.plugins import BasePlugin
15from mkdocs_spellcheck.loggers import get_plugin_logger
16from mkdocs_spellcheck.words import get_words
18if TYPE_CHECKING:
19 from mkdocs.config.defaults import MkDocsConfig
20 from mkdocs.structure.pages import Page
22 from mkdocs_spellcheck.backends import Backend
24logger = get_plugin_logger(__name__)
27def load_backend(name: str) -> type[Backend]:
28 """Load the specified backend.
30 This function imports the specified backend and returns its class.
31 It is important not to import the backends at the top level, as
32 they may not be installed.
34 Arguments:
35 name: The name of the backend to load.
37 Returns:
38 The backend class.
39 """
40 if name == "symspellpy":
41 from mkdocs_spellcheck.backends import symspellpy
43 return symspellpy.SymspellpyBackend
45 if name == "codespell":
46 from mkdocs_spellcheck.backends import codespell
48 return codespell.CodespellBackend
50 raise ValueError(f"Unknown backend: {name}")
53class SpellCheckPlugin(BasePlugin):
54 """A `mkdocs` plugin.
56 This plugin defines the following event hooks:
58 - `on_config`
59 - `on_page_content`
61 Check the [Developing Plugins](https://www.mkdocs.org/user-guide/plugins/#developing-plugins) page of `mkdocs`
62 for more information about its plugin system.
63 """
65 config_scheme: tuple[tuple[str, MkType], ...] = (
66 ("strict_only", MkType(bool, default=False)),
67 ("backends", MkType(list, default=["symspellpy"])),
68 ("known_words", MkType((str, list), default=[])),
69 ("skip_files", MkType(list, default=[])),
70 ("min_length", MkType(int, default=2)),
71 ("max_capital", MkType(int, default=1)),
72 ("ignore_code", MkType(bool, default=True)),
73 ("allow_unicode", MkType(bool, default=False)),
74 )
76 def __init__(self) -> None: # noqa: D107
77 self.known_words: set[str] = set()
78 super().__init__()
80 def on_config(self, config: MkDocsConfig) -> MkDocsConfig | None:
81 """Load words to ignore.
83 Hook for the [`on_config` event](https://www.mkdocs.org/user-guide/plugins/#on_config).
85 Arguments:
86 config: The MkDocs config object.
88 Returns:
89 The modified config.
90 """
91 self.strict_only = self.config["strict_only"]
92 self.backends_config = self.config["backends"]
93 self.skip_files = self.config["skip_files"]
94 self.min_length = self.config["min_length"]
95 self.max_capital = self.config["max_capital"]
96 self.ignore_code = self.config["ignore_code"]
97 self.allow_unicode = self.config["allow_unicode"]
98 self.run = config["strict"] or not self.strict_only
100 if not self.run:
101 return config
103 known_words = self.config["known_words"]
104 if isinstance(known_words, str):
105 self.known_words |= set(Path(config["docs_dir"], known_words).read_text().splitlines())
106 else:
107 self.known_words |= set(known_words)
109 self.backends = {}
110 for backend_conf in self.backends_config:
111 if isinstance(backend_conf, str):
112 backend_name = backend_conf
113 backend_config = {}
114 else:
115 backend_name, backend_config = next(iter(backend_conf.items()))
116 self.backends[backend_name] = load_backend(backend_name)(
117 known_words=self.known_words,
118 config=backend_config,
119 )
121 return config
123 def on_page_content(self, html: str, page: Page, **kwargs: Any) -> None: # noqa: ARG002
124 """Spell check everything.
126 Hook for the [`on_page_content` event](https://www.mkdocs.org/user-guide/plugins/#on_page_content).
128 Arguments:
129 html: The HTML text.
130 page: The page instance.
131 **kwargs: Additional arguments passed by MkDocs.
132 """
133 if self.run and not any(fnmatch(page.file.src_path, pattern) for pattern in self.skip_files):
134 words = get_words(
135 html,
136 known_words=self.known_words,
137 min_length=self.min_length,
138 max_capital=self.max_capital,
139 ignore_code=self.ignore_code,
140 allow_unicode=self.allow_unicode,
141 )
142 for word in words:
143 for backend in self.backends.values():
144 backend.check(page, word)