mkdocs-webis 0.1.2__tar.gz → 0.1.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. {mkdocs_webis-0.1.2 → mkdocs_webis-0.1.3}/PKG-INFO +2 -1
  2. {mkdocs_webis-0.1.2 → mkdocs_webis-0.1.3}/README.md +1 -0
  3. {mkdocs_webis-0.1.2 → mkdocs_webis-0.1.3}/docs/citations.md +1 -0
  4. {mkdocs_webis-0.1.2 → mkdocs_webis-0.1.3}/mkdocs_webis/bibliography.py +35 -5
  5. {mkdocs_webis-0.1.2 → mkdocs_webis-0.1.3}/mkdocs_webis/plugin.py +12 -0
  6. {mkdocs_webis-0.1.2 → mkdocs_webis-0.1.3}/mkdocs_webis.egg-info/PKG-INFO +2 -1
  7. mkdocs_webis-0.1.3/mkdocs_webis.egg-info/scm_version.json +8 -0
  8. {mkdocs_webis-0.1.2 → mkdocs_webis-0.1.3}/tests/test_bibliography.py +27 -0
  9. {mkdocs_webis-0.1.2 → mkdocs_webis-0.1.3}/tests/test_plugin.py +19 -0
  10. mkdocs_webis-0.1.2/mkdocs_webis.egg-info/scm_version.json +0 -8
  11. {mkdocs_webis-0.1.2 → mkdocs_webis-0.1.3}/.github/dependabot.yml +0 -0
  12. {mkdocs_webis-0.1.2 → mkdocs_webis-0.1.3}/.github/release.yml +0 -0
  13. {mkdocs_webis-0.1.2 → mkdocs_webis-0.1.3}/.github/workflows/ci.yml +0 -0
  14. {mkdocs_webis-0.1.2 → mkdocs_webis-0.1.3}/.gitignore +0 -0
  15. {mkdocs_webis-0.1.2 → mkdocs_webis-0.1.3}/LICENSE +0 -0
  16. {mkdocs_webis-0.1.2 → mkdocs_webis-0.1.3}/docs/index.md +0 -0
  17. {mkdocs_webis-0.1.2 → mkdocs_webis-0.1.3}/docs/references.bib +0 -0
  18. {mkdocs_webis-0.1.2 → mkdocs_webis-0.1.3}/docs/style.md +0 -0
  19. {mkdocs_webis-0.1.2 → mkdocs_webis-0.1.3}/mkdocs.yml +0 -0
  20. {mkdocs_webis-0.1.2 → mkdocs_webis-0.1.3}/mkdocs_webis/__init__.py +0 -0
  21. {mkdocs_webis-0.1.2 → mkdocs_webis-0.1.3}/mkdocs_webis/assets/favicon.png +0 -0
  22. {mkdocs_webis-0.1.2 → mkdocs_webis-0.1.3}/mkdocs_webis/assets/webis-logo.png +0 -0
  23. {mkdocs_webis-0.1.2 → mkdocs_webis-0.1.3}/mkdocs_webis/assets/webis.css +0 -0
  24. {mkdocs_webis-0.1.2 → mkdocs_webis-0.1.3}/mkdocs_webis/py.typed +0 -0
  25. {mkdocs_webis-0.1.2 → mkdocs_webis-0.1.3}/mkdocs_webis/templates/partials/header.html +0 -0
  26. {mkdocs_webis-0.1.2 → mkdocs_webis-0.1.3}/mkdocs_webis/templates/partials/tabs.html +0 -0
  27. {mkdocs_webis-0.1.2 → mkdocs_webis-0.1.3}/mkdocs_webis/templates/partials/webis-nav-item.html +0 -0
  28. {mkdocs_webis-0.1.2 → mkdocs_webis-0.1.3}/mkdocs_webis.egg-info/SOURCES.txt +0 -0
  29. {mkdocs_webis-0.1.2 → mkdocs_webis-0.1.3}/mkdocs_webis.egg-info/dependency_links.txt +0 -0
  30. {mkdocs_webis-0.1.2 → mkdocs_webis-0.1.3}/mkdocs_webis.egg-info/entry_points.txt +0 -0
  31. {mkdocs_webis-0.1.2 → mkdocs_webis-0.1.3}/mkdocs_webis.egg-info/requires.txt +0 -0
  32. {mkdocs_webis-0.1.2 → mkdocs_webis-0.1.3}/mkdocs_webis.egg-info/scm_file_list.json +0 -0
  33. {mkdocs_webis-0.1.2 → mkdocs_webis-0.1.3}/mkdocs_webis.egg-info/top_level.txt +0 -0
  34. {mkdocs_webis-0.1.2 → mkdocs_webis-0.1.3}/pyproject.toml +0 -0
  35. {mkdocs_webis-0.1.2 → mkdocs_webis-0.1.3}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mkdocs-webis
3
- Version: 0.1.2
3
+ Version: 0.1.3
4
4
  Summary: Webis style and BibTeX citations for MkDocs Material.
5
5
  Author-email: Jan Heinrich Merker <heinrich.merker@uni-jena.de>
6
6
  License-Expression: MIT
@@ -86,6 +86,7 @@ Some studies use axioms for re-ranking [@hagen2016axiomatic, @bondarenko2022axio
86
86
  Citations are rendered as gray author–year links in brackets (e.g., "[Fang et al., 2004]" or "[Hagen et al., 2016; Bondarenko et al., 2022]"), like on the Webis slides.
87
87
  They link to a "References" section at the bottom of the same page, which lists all references cited on that page.
88
88
  Multiple keys are separated by commas or semicolons.
89
+ Citations also work in API docstrings rendered by [mkdocstrings](https://mkdocstrings.github.io/).
89
90
 
90
91
  We recommend copying BibTeX entries from [DBLP](https://dblp.org/).
91
92
 
@@ -54,6 +54,7 @@ Some studies use axioms for re-ranking [@hagen2016axiomatic, @bondarenko2022axio
54
54
  Citations are rendered as gray author–year links in brackets (e.g., "[Fang et al., 2004]" or "[Hagen et al., 2016; Bondarenko et al., 2022]"), like on the Webis slides.
55
55
  They link to a "References" section at the bottom of the same page, which lists all references cited on that page.
56
56
  Multiple keys are separated by commas or semicolons.
57
+ Citations also work in API docstrings rendered by [mkdocstrings](https://mkdocstrings.github.io/).
57
58
 
58
59
  We recommend copying BibTeX entries from [DBLP](https://dblp.org/).
59
60
 
@@ -22,6 +22,7 @@ A journal article looks like this: [@fang2011diagnostic].
22
22
 
23
23
  - Separate multiple keys by commas (semicolons work, too).
24
24
  - Citations inside inline code or code blocks are not replaced, as in the example above.
25
+ - Citations in content that other plugins insert while rendering a page (e.g., API docstrings from [mkdocstrings](https://mkdocstrings.github.io/)) are replaced, too, and their references are added to the "References" section.
25
26
  - Unknown keys and invalid BibTeX files fail the build.
26
27
  - LaTeX markup in the BibTeX fields (e.g., `{\"{o}}` for "ö") is converted to plain text.
27
28
 
@@ -5,13 +5,15 @@ Citations from a BibTeX file.
5
5
  e.g., "[Fang et al., 2004]".
6
6
  - Each page that cites at least one reference gets a references section at the
7
7
  bottom, listing the cited references, including the BibTeX of each entry.
8
+ - Citations that only appear in the rendered HTML (e.g., from docstrings
9
+ inserted by mkdocstrings) are replaced as well, and their references are added.
8
10
  """
9
11
 
10
12
  from collections.abc import Iterable, Mapping
11
13
  from dataclasses import dataclass, field
12
14
  from html import escape
13
15
  from pathlib import Path
14
- from re import DOTALL, MULTILINE, Match, compile as re_compile
16
+ from re import DOTALL, MULTILINE, Match, Pattern, compile as re_compile
15
17
  from typing import Final
16
18
 
17
19
  from bibtexparser.entrypoint import parse_string
@@ -26,6 +28,8 @@ _PATTERN_CITATION_SEPARATOR: Final = re_compile(r"\s*[,;]\s*")
26
28
  _PATTERN_CODE: Final = re_compile(
27
29
  r"(^[ \t]*```.*?^[ \t]*```|`[^`\n]+`)", MULTILINE | DOTALL
28
30
  )
31
+ # Code blocks and inline code in HTML, in which citations are not replaced.
32
+ _PATTERN_CODE_HTML: Final = re_compile(r"(<pre\b.*?</pre>|<code\b.*?</code>)", DOTALL)
29
33
  # Fields that are not LaTeX (where, e.g., "%" is not a comment).
30
34
  _VERBATIM_FIELDS: Final = frozenset({"url", "doi", "eprint"})
31
35
 
@@ -239,16 +243,20 @@ class Bibliography:
239
243
  blocks.extend(self.render_reference(entry) for entry in entries)
240
244
  return "\n\n".join(blocks) + "\n"
241
245
 
242
- def process(self, markdown: str) -> str:
243
- """Replace citations and append the references section, if any."""
246
+ def _replace_citations(self, text: str, code_pattern: Pattern[str]) -> str:
247
+ """Replace citations outside of code and remember the cited keys."""
244
248
  self._cited_keys = set()
245
249
  # Odd indices of the split are code, which is kept as is.
246
- markdown = "".join(
250
+ return "".join(
247
251
  part
248
252
  if index % 2 == 1
249
253
  else _PATTERN_CITATION.sub(self._replace_citation, part)
250
- for index, part in enumerate(_PATTERN_CODE.split(markdown))
254
+ for index, part in enumerate(code_pattern.split(text))
251
255
  )
256
+
257
+ def process(self, markdown: str) -> str:
258
+ """Replace citations and append the references section, if any."""
259
+ markdown = self._replace_citations(markdown, _PATTERN_CODE)
252
260
  if self._cited_keys:
253
261
  markdown = (
254
262
  markdown.rstrip()
@@ -256,3 +264,25 @@ class Bibliography:
256
264
  + self.render_references(self._cited_keys)
257
265
  )
258
266
  return markdown
267
+
268
+ def process_html(self, html: str) -> str:
269
+ """
270
+ Replace citations that remain in the rendered HTML of a page (e.g., from
271
+ docstrings) and append their references, unless already listed.
272
+ """
273
+ html = self._replace_citations(html, _PATTERN_CODE_HTML)
274
+ missing = [
275
+ self.entries[key]
276
+ for key in self._cited_keys
277
+ if f'<div class="reference" id="{escape(key)}">' not in html
278
+ ]
279
+ if not missing:
280
+ return html
281
+ blocks = [html.rstrip()]
282
+ if 'id="references"' not in html:
283
+ blocks.append('<h2 id="references">References</h2>')
284
+ blocks.extend(
285
+ self.render_reference(entry)
286
+ for entry in sorted(missing, key=lambda e: e.sort_key)
287
+ )
288
+ return "\n".join(blocks) + "\n"
@@ -115,6 +115,18 @@ class WebisPlugin(BasePlugin[WebisPluginConfig]):
115
115
  except BibliographyError as error:
116
116
  raise PluginError(f"{error} (in {page.file.src_uri})") from error
117
117
 
118
+ def on_page_content(
119
+ self, html: str, /, *, page: Page, config: MkDocsConfig, files: Files
120
+ ) -> str | None:
121
+ # Citations from content that other plugins insert while rendering the
122
+ # Markdown (e.g., docstrings from mkdocstrings).
123
+ if self._bibliography is None:
124
+ return html
125
+ try:
126
+ return self._bibliography.process_html(html)
127
+ except BibliographyError as error:
128
+ raise PluginError(f"{error} (in {page.file.src_uri})") from error
129
+
118
130
 
119
131
  def files_resource(name: str) -> Traversable:
120
132
  return files("mkdocs_webis").joinpath("assets", name)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mkdocs-webis
3
- Version: 0.1.2
3
+ Version: 0.1.3
4
4
  Summary: Webis style and BibTeX citations for MkDocs Material.
5
5
  Author-email: Jan Heinrich Merker <heinrich.merker@uni-jena.de>
6
6
  License-Expression: MIT
@@ -86,6 +86,7 @@ Some studies use axioms for re-ranking [@hagen2016axiomatic, @bondarenko2022axio
86
86
  Citations are rendered as gray author–year links in brackets (e.g., "[Fang et al., 2004]" or "[Hagen et al., 2016; Bondarenko et al., 2022]"), like on the Webis slides.
87
87
  They link to a "References" section at the bottom of the same page, which lists all references cited on that page.
88
88
  Multiple keys are separated by commas or semicolons.
89
+ Citations also work in API docstrings rendered by [mkdocstrings](https://mkdocstrings.github.io/).
89
90
 
90
91
  We recommend copying BibTeX entries from [DBLP](https://dblp.org/).
91
92
 
@@ -0,0 +1,8 @@
1
+ {
2
+ "tag": "0.1.3",
3
+ "distance": 0,
4
+ "node": "g4156484d3dff0dbb4a969668f46252cf27f66139",
5
+ "dirty": false,
6
+ "branch": "HEAD",
7
+ "node_date": "2026-10-20"
8
+ }
@@ -149,3 +149,30 @@ def test_process_unknown_key() -> None:
149
149
  bibliography = Bibliography(entries=parse_bibtex(BIBTEX))
150
150
  with raises(BibliographyError):
151
151
  bibliography.process("See [@unknown].")
152
+
153
+
154
+ def test_process_html_citations() -> None:
155
+ bibliography = Bibliography(entries=parse_bibtex(BIBTEX))
156
+ html = bibliography.process_html("<p>See [@lv2011lower; @fang2004formal].</p>")
157
+ assert '<a href="#fang2004formal">Fang et al., 2004</a>' in html
158
+ assert '<h2 id="references">References</h2>' in html
159
+ # Sorted by first author.
160
+ assert html.index('id="fang2004formal"') < html.index('id="lv2011lower"')
161
+
162
+
163
+ def test_process_html_adds_missing_references() -> None:
164
+ bibliography = Bibliography(entries=parse_bibtex(BIBTEX))
165
+ html = bibliography.process_html(
166
+ "<p>See [@fang2004formal, @lv2011lower].</p>\n"
167
+ '<h2 id="references">References</h2>\n'
168
+ '<div class="reference" id="fang2004formal">...</div>'
169
+ )
170
+ assert html.count('id="references"') == 1
171
+ assert html.count('id="fang2004formal"') == 1
172
+ assert '<div class="reference" id="lv2011lower">' in html
173
+
174
+
175
+ def test_process_html_ignores_code() -> None:
176
+ bibliography = Bibliography(entries=parse_bibtex(BIBTEX))
177
+ html = "<p>Use <code>[@key]</code> to cite.</p>\n<pre><code>[@unknown]\n</code></pre>"
178
+ assert bibliography.process_html(html) == html
@@ -115,3 +115,22 @@ def test_unknown_citation(tmp_path: Path) -> None:
115
115
  def test_missing_bibliography(tmp_path: Path) -> None:
116
116
  with raises((PluginError, Abort)):
117
117
  _build(tmp_path, ":\n bibliography: missing.bib")
118
+
119
+
120
+ def test_citations_in_rendered_html(tmp_path: Path) -> None:
121
+ # Simulate content inserted while rendering the Markdown (e.g., docstrings).
122
+ (tmp_path / "hooks.py").write_text(
123
+ "def on_page_markdown(markdown, **kwargs):\n"
124
+ " return markdown.replace('DOCSTRING', '<p>[@doe2020test]</p>')\n"
125
+ )
126
+ site_dir = _build(
127
+ tmp_path,
128
+ ":\n bibliography: references.bib",
129
+ page="# Home\n\nDOCSTRING\n",
130
+ extra_config="hooks:\n - hooks.py\n",
131
+ )
132
+ html = (site_dir / "index.html").read_text()
133
+ assert "[@doe2020test]" not in html
134
+ assert '&#91;<a href="#doe2020test">Doe, 2020</a>&#93;' in html
135
+ assert '<h2 id="references">References</h2>' in html
136
+ assert '<div class="reference" id="doe2020test">' in html
@@ -1,8 +0,0 @@
1
- {
2
- "tag": "0.1.2",
3
- "distance": 0,
4
- "node": "g9ab792225cf6fd2f9fb30db024df95bce7b5a118",
5
- "dirty": false,
6
- "branch": "HEAD",
7
- "node_date": "2026-10-16"
8
- }
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes