pytex-preprocessor 1.0.5__tar.gz → 1.0.6__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 (148) hide show
  1. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/PKG-INFO +1 -1
  2. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/pyproject.toml +1 -1
  3. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_builder/tectonic.py +61 -38
  4. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_preprocessor.egg-info/PKG-INFO +1 -1
  5. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/LICENSE +0 -0
  6. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/README.md +0 -0
  7. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/setup.cfg +0 -0
  8. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex/__init__.py +0 -0
  9. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex/commands/__init__.py +0 -0
  10. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex/commands/biblatex.py +0 -0
  11. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex/commands/builtin.py +0 -0
  12. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex/commands/captions.py +0 -0
  13. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex/commands/cleveref.py +0 -0
  14. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex/commands/colors.py +0 -0
  15. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex/commands/conditionals.py +0 -0
  16. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex/commands/counters.py +0 -0
  17. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex/commands/definitions.py +0 -0
  18. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex/commands/floats.py +0 -0
  19. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex/commands/font.py +0 -0
  20. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex/commands/fontawesome.py +0 -0
  21. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex/commands/fontspec.py +0 -0
  22. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex/commands/geometry.py +0 -0
  23. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex/commands/glossaries.py +0 -0
  24. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex/commands/graphics.py +0 -0
  25. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex/commands/hooks.py +0 -0
  26. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex/commands/hyperref.py +0 -0
  27. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex/commands/lengths.py +0 -0
  28. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex/commands/listings.py +0 -0
  29. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex/commands/mdframed.py +0 -0
  30. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex/commands/picture.py +0 -0
  31. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex/commands/setspace.py +0 -0
  32. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex/commands/tables.py +0 -0
  33. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex/helpers/__init__.py +0 -0
  34. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex/helpers/coerce.py +0 -0
  35. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex/helpers/parenting.py +0 -0
  36. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex/helpers/sanitize.py +0 -0
  37. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex/helpers/with_package.py +0 -0
  38. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex/interface/__init__.py +0 -0
  39. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex/interface/control_sequence.py +0 -0
  40. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex/interface/package.py +0 -0
  41. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex/interface/tex.py +0 -0
  42. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex/model/__init__.py +0 -0
  43. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex/model/color.py +0 -0
  44. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex/model/comment.py +0 -0
  45. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex/model/concat.py +0 -0
  46. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex/model/control_sequence.py +0 -0
  47. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex/model/document.py +0 -0
  48. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex/model/document_class.py +0 -0
  49. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex/model/empty.py +0 -0
  50. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex/model/environment.py +0 -0
  51. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex/model/image.py +0 -0
  52. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex/model/include.py +0 -0
  53. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex/model/length.py +0 -0
  54. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex/model/math.py +0 -0
  55. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex/model/package.py +0 -0
  56. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex/model/raw.py +0 -0
  57. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex/packages.py +0 -0
  58. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex/registry.py +0 -0
  59. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex/template.py +0 -0
  60. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_analyze/__init__.py +0 -0
  61. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_analyze/analyze.py +0 -0
  62. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_analyze/optimize.py +0 -0
  63. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_api/__init__.py +0 -0
  64. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_api/_compile.py +0 -0
  65. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_api/_models.py +0 -0
  66. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_api/_policy.py +0 -0
  67. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_api/_render.py +0 -0
  68. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_api/_sandbox.py +0 -0
  69. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_api/_security.py +0 -0
  70. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_api/sandbox_init.py +0 -0
  71. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_builder/__init__.py +0 -0
  72. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_builder/build.py +0 -0
  73. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_builder/console.py +0 -0
  74. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_builder/render.py +0 -0
  75. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_builder/tex2py.py +0 -0
  76. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_builder/tree.py +0 -0
  77. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_builder/variants.py +0 -0
  78. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_components/__init__.py +0 -0
  79. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_components/boxes.py +0 -0
  80. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_components/citations.py +0 -0
  81. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_components/cleveref_names.py +0 -0
  82. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_components/pagebreak.py +0 -0
  83. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_components/voting.py +0 -0
  84. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_components/watermark.py +0 -0
  85. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_components/wordcount.py +0 -0
  86. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_hsrtreport/__init__.py +0 -0
  87. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_hsrtreport/assets/fonts/Blender/Blender-Bold.ttf +0 -0
  88. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_hsrtreport/assets/fonts/Blender/Blender-BoldItalic.ttf +0 -0
  89. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_hsrtreport/assets/fonts/Blender/Blender-Book.ttf +0 -0
  90. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_hsrtreport/assets/fonts/Blender/Blender-BookItalic.ttf +0 -0
  91. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_hsrtreport/assets/fonts/Blender/Blender-Medium.ttf +0 -0
  92. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_hsrtreport/assets/fonts/Blender/Blender-MediumItalic.ttf +0 -0
  93. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_hsrtreport/assets/fonts/Blender/Blender-Strong.ttf +0 -0
  94. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_hsrtreport/assets/fonts/Blender/Blender-Thin.ttf +0 -0
  95. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_hsrtreport/assets/fonts/Blender/Blender-ThinItalic.ttf +0 -0
  96. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_hsrtreport/assets/fonts/DIN/DIN-Black.ttf +0 -0
  97. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_hsrtreport/assets/fonts/DIN/DIN-Bold.ttf +0 -0
  98. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_hsrtreport/assets/fonts/DIN/DIN-BoldItalic.ttf +0 -0
  99. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_hsrtreport/assets/fonts/DIN/DIN-Italic.ttf +0 -0
  100. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_hsrtreport/assets/fonts/DIN/DIN-Medium.ttf +0 -0
  101. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_hsrtreport/assets/fonts/DIN/DIN-Regular.ttf +0 -0
  102. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_hsrtreport/assets/fonts/Times New Roman.ttf +0 -0
  103. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_hsrtreport/assets/logos/ASTA.svg +0 -0
  104. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_hsrtreport/assets/logos/DUMMY.png +0 -0
  105. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_hsrtreport/assets/logos/DUMMY_FOOT.png +0 -0
  106. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_hsrtreport/assets/logos/ECHO.svg +0 -0
  107. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_hsrtreport/assets/logos/HSRT.pdf +0 -0
  108. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_hsrtreport/assets/logos/INF.pdf +0 -0
  109. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_hsrtreport/assets/logos/MAKERS-Icon.svg +0 -0
  110. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_hsrtreport/assets/logos/MAKERS-RAlign.svg +0 -0
  111. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_hsrtreport/assets/logos/MAKERS.svg +0 -0
  112. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_hsrtreport/assets/logos/STUPA.pdf +0 -0
  113. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_hsrtreport/assets/logos/Skyline.pdf +0 -0
  114. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_hsrtreport/colors.py +0 -0
  115. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_hsrtreport/document.py +0 -0
  116. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_hsrtreport/fonts.py +0 -0
  117. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_hsrtreport/glossary.py +0 -0
  118. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_hsrtreport/hyperref_config.py +0 -0
  119. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_hsrtreport/listings.py +0 -0
  120. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_hsrtreport/logos.py +0 -0
  121. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_hsrtreport/pagesetup.py +0 -0
  122. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_hsrtreport/tex/pagesetup.tex +0 -0
  123. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_hsrtreport/titlepage.py +0 -0
  124. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_hsrtreport/variants.py +0 -0
  125. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_koma/__init__.py +0 -0
  126. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_koma/commands.py +0 -0
  127. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_koma/document.py +0 -0
  128. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_markdown/__init__.py +0 -0
  129. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_markdown/convert.py +0 -0
  130. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_markdown/escape.py +0 -0
  131. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_markdown/frontmatter.py +0 -0
  132. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_markdown/glyphs.py +0 -0
  133. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_markdown/protocol/__init__.py +0 -0
  134. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_markdown/protocol/convert.py +0 -0
  135. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_markdown/protocol/document.py +0 -0
  136. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_markdown/protocol/entries.py +0 -0
  137. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_markdown/protocol/header.py +0 -0
  138. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_markdown/protocol/shortcodes.py +0 -0
  139. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_markdown/protocol/signatures.py +0 -0
  140. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_preprocessor.egg-info/SOURCES.txt +0 -0
  141. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_preprocessor.egg-info/dependency_links.txt +0 -0
  142. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_preprocessor.egg-info/entry_points.txt +0 -0
  143. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_preprocessor.egg-info/requires.txt +0 -0
  144. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_preprocessor.egg-info/top_level.txt +0 -0
  145. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_protocol/__init__.py +0 -0
  146. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_protocol/frontmatter.py +0 -0
  147. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_tikz/__init__.py +0 -0
  148. {pytex_preprocessor-1.0.5 → pytex_preprocessor-1.0.6}/src/pytex_tikz/tikz.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pytex-preprocessor
3
- Version: 1.0.5
3
+ Version: 1.0.6
4
4
  Summary: Type-safe LaTeX document generation with Python
5
5
  Author-email: Frederik Beimgraben <frederik@beimgraben.net>
6
6
  License: GNU GENERAL PUBLIC LICENSE
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "pytex-preprocessor"
7
- version = "1.0.5"
7
+ version = "1.0.6"
8
8
  authors = [
9
9
  { name="Frederik Beimgraben", email="frederik@beimgraben.net" },
10
10
  ]
@@ -262,26 +262,6 @@ def _biber_cached(version: str) -> Path:
262
262
  return CACHE_DIR / "biber" / version / name
263
263
 
264
264
 
265
- def _biber_sources(version: str) -> list[tuple[str, str | None]]:
266
- """``(url, expected_sha256 or None)`` to try in order.
267
-
268
- For each platform candidate the mirror is tried first, then SourceForge.
269
- """
270
- sources: list[tuple[str, str | None]] = []
271
- for sf_dir, filename, asset in _biber_candidates(version):
272
- sha = BIBER_SHA256.get(asset)
273
- sources.append((BIBER_MIRROR_URL.format(asset=asset), sha))
274
- sources.append(
275
- (
276
- BIBER_RELEASE_URL.format(
277
- version=version, sf_dir=sf_dir, filename=filename
278
- ),
279
- sha,
280
- )
281
- )
282
- return sources
283
-
284
-
285
265
  def _is_biber_member(name: str) -> bool:
286
266
  """Whether an archive member is the biber executable.
287
267
 
@@ -343,11 +323,36 @@ def _download_to(url: str, dest: Path, sha: str | None, console: Console) -> boo
343
323
  return True
344
324
 
345
325
 
326
+ def _biber_runs(binary: Path) -> bool:
327
+ """``True`` if ``binary`` actually executes here (``biber --version`` exits 0).
328
+
329
+ The musl build is offered first (no glibc shared-lib deps) but is *dynamically*
330
+ linked against the musl loader; on a glibc-only host (e.g. Debian-slim) it
331
+ cannot exec at all ("No such file or directory"). Running it is the only
332
+ reliable cross-check, so we verify and fall back to the glibc build."""
333
+ try:
334
+ proc = subprocess.run(
335
+ [str(binary), "--version"], capture_output=True, timeout=30
336
+ )
337
+ except OSError:
338
+ return False
339
+ return proc.returncode == 0
340
+
341
+
346
342
  def _ensure_biber(version: str, console: Console) -> Path:
347
- """Return a path to biber *version*, downloading from the mirror or SourceForge."""
343
+ """Return a path to biber *version*, downloading from the mirror or SourceForge.
344
+
345
+ Each platform candidate (musl first, then glibc) is downloaded, extracted and
346
+ **test-run**; the first one that actually executes here is cached. This makes
347
+ the choice robust on hosts that lack the musl loader or a glibc dependency."""
348
348
  cached = _biber_cached(version)
349
349
  if cached.exists():
350
- return cached
350
+ # Re-validate: a cache poisoned by an earlier release (e.g. a musl binary
351
+ # that cannot exec here) must be replaced, not blindly reused.
352
+ if _biber_runs(cached):
353
+ return cached
354
+ console.detail("cached biber does not execute here; re-downloading")
355
+ cached.unlink(missing_ok=True)
351
356
 
352
357
  if not shutil.which("curl"):
353
358
  raise BuildError(
@@ -357,28 +362,46 @@ def _ensure_biber(version: str, console: Console) -> Path:
357
362
  console.step(f"Downloading biber {version}")
358
363
  cached.parent.mkdir(parents=True, exist_ok=True)
359
364
  tmp = cached.parent / "biber.download"
365
+ cand = cached.parent / "biber.candidate"
366
+ last: str | None = None
360
367
  try:
361
- downloaded = False
362
- for url, sha in _biber_sources(version):
363
- console.detail(f"source: {url}")
364
- if _download_to(url, tmp, sha, console):
365
- downloaded = True
366
- break
367
- if not downloaded:
368
- raise BuildError(
369
- f"failed to download biber {version} from the mirror or SourceForge"
370
- )
371
- cached.write_bytes(_extract_biber_binary(tmp, version))
368
+ for sf_dir, filename, asset in _biber_candidates(version):
369
+ sha = BIBER_SHA256.get(asset)
370
+ urls = [
371
+ BIBER_MIRROR_URL.format(asset=asset),
372
+ BIBER_RELEASE_URL.format(
373
+ version=version, sf_dir=sf_dir, filename=filename
374
+ ),
375
+ ]
376
+ downloaded = False
377
+ for url in urls:
378
+ console.detail(f"source: {url}")
379
+ if _download_to(url, tmp, sha, console):
380
+ downloaded = True
381
+ break
382
+ if not downloaded:
383
+ last = f"{asset}: download failed"
384
+ continue
385
+ try:
386
+ cand.write_bytes(_extract_biber_binary(tmp, version))
387
+ cand.chmod(0o755)
388
+ except Exception as exc:
389
+ last = f"{asset}: extract failed ({exc})"
390
+ continue
391
+ if _biber_runs(cand):
392
+ cand.replace(cached)
393
+ cached.chmod(0o755)
394
+ return cached
395
+ last = f"{asset}: does not execute on this platform"
396
+ cand.unlink(missing_ok=True)
397
+ raise BuildError(f"failed to obtain a working biber {version} ({last})")
372
398
  except Exception:
373
399
  if cached.exists():
374
400
  cached.unlink()
375
401
  raise
376
402
  finally:
377
- if tmp.exists():
378
- tmp.unlink()
379
-
380
- cached.chmod(0o755)
381
- return cached
403
+ tmp.unlink(missing_ok=True)
404
+ cand.unlink(missing_ok=True)
382
405
 
383
406
 
384
407
  def biber_for_build(build_dir: Path, job: str, console: Console) -> Path | None:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pytex-preprocessor
3
- Version: 1.0.5
3
+ Version: 1.0.6
4
4
  Summary: Type-safe LaTeX document generation with Python
5
5
  Author-email: Frederik Beimgraben <frederik@beimgraben.net>
6
6
  License: GNU GENERAL PUBLIC LICENSE