pytex-preprocessor 1.0.3__tar.gz → 1.0.5__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.3 → pytex_preprocessor-1.0.5}/PKG-INFO +1 -1
  2. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/pyproject.toml +1 -1
  3. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_api/_compile.py +48 -1
  4. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_builder/tectonic.py +17 -9
  5. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_preprocessor.egg-info/PKG-INFO +1 -1
  6. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/LICENSE +0 -0
  7. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/README.md +0 -0
  8. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/setup.cfg +0 -0
  9. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex/__init__.py +0 -0
  10. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex/commands/__init__.py +0 -0
  11. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex/commands/biblatex.py +0 -0
  12. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex/commands/builtin.py +0 -0
  13. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex/commands/captions.py +0 -0
  14. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex/commands/cleveref.py +0 -0
  15. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex/commands/colors.py +0 -0
  16. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex/commands/conditionals.py +0 -0
  17. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex/commands/counters.py +0 -0
  18. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex/commands/definitions.py +0 -0
  19. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex/commands/floats.py +0 -0
  20. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex/commands/font.py +0 -0
  21. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex/commands/fontawesome.py +0 -0
  22. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex/commands/fontspec.py +0 -0
  23. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex/commands/geometry.py +0 -0
  24. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex/commands/glossaries.py +0 -0
  25. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex/commands/graphics.py +0 -0
  26. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex/commands/hooks.py +0 -0
  27. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex/commands/hyperref.py +0 -0
  28. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex/commands/lengths.py +0 -0
  29. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex/commands/listings.py +0 -0
  30. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex/commands/mdframed.py +0 -0
  31. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex/commands/picture.py +0 -0
  32. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex/commands/setspace.py +0 -0
  33. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex/commands/tables.py +0 -0
  34. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex/helpers/__init__.py +0 -0
  35. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex/helpers/coerce.py +0 -0
  36. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex/helpers/parenting.py +0 -0
  37. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex/helpers/sanitize.py +0 -0
  38. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex/helpers/with_package.py +0 -0
  39. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex/interface/__init__.py +0 -0
  40. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex/interface/control_sequence.py +0 -0
  41. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex/interface/package.py +0 -0
  42. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex/interface/tex.py +0 -0
  43. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex/model/__init__.py +0 -0
  44. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex/model/color.py +0 -0
  45. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex/model/comment.py +0 -0
  46. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex/model/concat.py +0 -0
  47. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex/model/control_sequence.py +0 -0
  48. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex/model/document.py +0 -0
  49. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex/model/document_class.py +0 -0
  50. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex/model/empty.py +0 -0
  51. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex/model/environment.py +0 -0
  52. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex/model/image.py +0 -0
  53. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex/model/include.py +0 -0
  54. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex/model/length.py +0 -0
  55. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex/model/math.py +0 -0
  56. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex/model/package.py +0 -0
  57. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex/model/raw.py +0 -0
  58. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex/packages.py +0 -0
  59. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex/registry.py +0 -0
  60. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex/template.py +0 -0
  61. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_analyze/__init__.py +0 -0
  62. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_analyze/analyze.py +0 -0
  63. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_analyze/optimize.py +0 -0
  64. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_api/__init__.py +0 -0
  65. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_api/_models.py +0 -0
  66. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_api/_policy.py +0 -0
  67. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_api/_render.py +0 -0
  68. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_api/_sandbox.py +0 -0
  69. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_api/_security.py +0 -0
  70. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_api/sandbox_init.py +0 -0
  71. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_builder/__init__.py +0 -0
  72. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_builder/build.py +0 -0
  73. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_builder/console.py +0 -0
  74. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_builder/render.py +0 -0
  75. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_builder/tex2py.py +0 -0
  76. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_builder/tree.py +0 -0
  77. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_builder/variants.py +0 -0
  78. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_components/__init__.py +0 -0
  79. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_components/boxes.py +0 -0
  80. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_components/citations.py +0 -0
  81. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_components/cleveref_names.py +0 -0
  82. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_components/pagebreak.py +0 -0
  83. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_components/voting.py +0 -0
  84. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_components/watermark.py +0 -0
  85. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_components/wordcount.py +0 -0
  86. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_hsrtreport/__init__.py +0 -0
  87. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_hsrtreport/assets/fonts/Blender/Blender-Bold.ttf +0 -0
  88. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_hsrtreport/assets/fonts/Blender/Blender-BoldItalic.ttf +0 -0
  89. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_hsrtreport/assets/fonts/Blender/Blender-Book.ttf +0 -0
  90. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_hsrtreport/assets/fonts/Blender/Blender-BookItalic.ttf +0 -0
  91. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_hsrtreport/assets/fonts/Blender/Blender-Medium.ttf +0 -0
  92. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_hsrtreport/assets/fonts/Blender/Blender-MediumItalic.ttf +0 -0
  93. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_hsrtreport/assets/fonts/Blender/Blender-Strong.ttf +0 -0
  94. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_hsrtreport/assets/fonts/Blender/Blender-Thin.ttf +0 -0
  95. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_hsrtreport/assets/fonts/Blender/Blender-ThinItalic.ttf +0 -0
  96. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_hsrtreport/assets/fonts/DIN/DIN-Black.ttf +0 -0
  97. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_hsrtreport/assets/fonts/DIN/DIN-Bold.ttf +0 -0
  98. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_hsrtreport/assets/fonts/DIN/DIN-BoldItalic.ttf +0 -0
  99. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_hsrtreport/assets/fonts/DIN/DIN-Italic.ttf +0 -0
  100. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_hsrtreport/assets/fonts/DIN/DIN-Medium.ttf +0 -0
  101. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_hsrtreport/assets/fonts/DIN/DIN-Regular.ttf +0 -0
  102. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_hsrtreport/assets/fonts/Times New Roman.ttf +0 -0
  103. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_hsrtreport/assets/logos/ASTA.svg +0 -0
  104. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_hsrtreport/assets/logos/DUMMY.png +0 -0
  105. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_hsrtreport/assets/logos/DUMMY_FOOT.png +0 -0
  106. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_hsrtreport/assets/logos/ECHO.svg +0 -0
  107. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_hsrtreport/assets/logos/HSRT.pdf +0 -0
  108. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_hsrtreport/assets/logos/INF.pdf +0 -0
  109. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_hsrtreport/assets/logos/MAKERS-Icon.svg +0 -0
  110. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_hsrtreport/assets/logos/MAKERS-RAlign.svg +0 -0
  111. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_hsrtreport/assets/logos/MAKERS.svg +0 -0
  112. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_hsrtreport/assets/logos/STUPA.pdf +0 -0
  113. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_hsrtreport/assets/logos/Skyline.pdf +0 -0
  114. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_hsrtreport/colors.py +0 -0
  115. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_hsrtreport/document.py +0 -0
  116. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_hsrtreport/fonts.py +0 -0
  117. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_hsrtreport/glossary.py +0 -0
  118. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_hsrtreport/hyperref_config.py +0 -0
  119. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_hsrtreport/listings.py +0 -0
  120. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_hsrtreport/logos.py +0 -0
  121. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_hsrtreport/pagesetup.py +0 -0
  122. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_hsrtreport/tex/pagesetup.tex +0 -0
  123. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_hsrtreport/titlepage.py +0 -0
  124. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_hsrtreport/variants.py +0 -0
  125. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_koma/__init__.py +0 -0
  126. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_koma/commands.py +0 -0
  127. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_koma/document.py +0 -0
  128. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_markdown/__init__.py +0 -0
  129. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_markdown/convert.py +0 -0
  130. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_markdown/escape.py +0 -0
  131. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_markdown/frontmatter.py +0 -0
  132. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_markdown/glyphs.py +0 -0
  133. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_markdown/protocol/__init__.py +0 -0
  134. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_markdown/protocol/convert.py +0 -0
  135. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_markdown/protocol/document.py +0 -0
  136. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_markdown/protocol/entries.py +0 -0
  137. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_markdown/protocol/header.py +0 -0
  138. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_markdown/protocol/shortcodes.py +0 -0
  139. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_markdown/protocol/signatures.py +0 -0
  140. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_preprocessor.egg-info/SOURCES.txt +0 -0
  141. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_preprocessor.egg-info/dependency_links.txt +0 -0
  142. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_preprocessor.egg-info/entry_points.txt +0 -0
  143. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_preprocessor.egg-info/requires.txt +0 -0
  144. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_preprocessor.egg-info/top_level.txt +0 -0
  145. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_protocol/__init__.py +0 -0
  146. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_protocol/frontmatter.py +0 -0
  147. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/src/pytex_tikz/__init__.py +0 -0
  148. {pytex_preprocessor-1.0.3 → pytex_preprocessor-1.0.5}/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.3
3
+ Version: 1.0.5
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.3"
7
+ version = "1.0.5"
8
8
  authors = [
9
9
  { name="Frederik Beimgraben", email="frederik@beimgraben.net" },
10
10
  ]
@@ -102,18 +102,55 @@ def _kill_group(proc: subprocess.Popen[str]) -> None: # pragma: no cover - timi
102
102
  proc.kill()
103
103
 
104
104
 
105
+ def _biber_env(
106
+ cmd: list[str], build_dir: Path, policy: TrustPolicy, console: Console
107
+ ) -> dict[str, str] | None:
108
+ """A child env whose PATH includes the biber matching the document's BCF.
109
+
110
+ Mirrors ``pytex_builder.tectonic.run_tectonic``: read the BCF (probing with a
111
+ no-op biber if it is not written yet) to pick the right biber release, then
112
+ ensure it (download+cache). Returns ``None`` (inherit env) for documents
113
+ without biblatex, or when biber can't be obtained (e.g. network disabled) —
114
+ tectonic then surfaces its own error rather than us masking it.
115
+ """
116
+ tex = build_dir.parent / f"{_JOB}.tex"
117
+ try:
118
+ source = tex.read_text(encoding="utf-8", errors="ignore")
119
+ except OSError:
120
+ return None
121
+ # No biblatex → tectonic never invokes biber; skip the extra probe pass.
122
+ if "biblatex" not in source:
123
+ return None
124
+ try:
125
+ from pytex_builder.tectonic import biber_for_build, env_with_biber, probe_bcf
126
+ except Exception: # pragma: no cover - import guard
127
+ return None
128
+ job = _JOB
129
+ try:
130
+ biber = biber_for_build(build_dir, job, console)
131
+ if biber is None and policy.allow_network:
132
+ probe_bcf(cmd)
133
+ biber = biber_for_build(build_dir, job, console)
134
+ except Exception as exc: # pragma: no cover - infra
135
+ console.warn(f"biber setup skipped: {exc}")
136
+ return None
137
+ return env_with_biber(biber) if biber is not None else None
138
+
139
+
105
140
  def _run_confined(
106
141
  cmd: list[str],
107
142
  cwd: Path,
108
143
  limits: BuildLimits,
109
144
  *,
110
145
  apply_rlimits: bool,
146
+ env: Mapping[str, str] | None = None,
111
147
  ) -> tuple[int, str]:
112
148
  """Run ``cmd`` with rlimits + a hard wall-clock kill; return (rc, output).
113
149
 
114
150
  ``apply_rlimits`` is the in-process ``setrlimit`` floor; it is switched off
115
151
  for the Podman path, where the container's cgroup flags do the capping (an
116
152
  rlimit on the ``podman`` *client* would not reach the build inside).
153
+ ``env`` overrides the child environment (e.g. a PATH that includes biber).
117
154
  """
118
155
  posix = os.name == "posix"
119
156
  preexec = make_rlimit_preexec(limits) if (apply_rlimits and posix) else None
@@ -126,6 +163,7 @@ def _run_confined(
126
163
  text=True,
127
164
  start_new_session=posix,
128
165
  preexec_fn=preexec,
166
+ env=dict(env) if env is not None else None,
129
167
  )
130
168
  except OSError as exc:
131
169
  raise CompileError(f"could not start tectonic: {exc}") from exc
@@ -256,8 +294,17 @@ def compile_to_pdf(
256
294
  )
257
295
  binary = _locate_tectonic(policy, console)
258
296
  cmd = build_tectonic_cmd(binary, tex_file, build_dir, policy)
297
+ # biblatex docs (report/protocol) make tectonic shell out to ``biber``;
298
+ # it is not bundled, so provide a version-matched one on PATH (probe the
299
+ # BCF, then download+cache the matching biber). Without it tectonic dies
300
+ # with "Running external tool biber ... No such file or directory".
301
+ biber_env = _biber_env(cmd, build_dir, policy, console)
259
302
  rc, out = _run_confined(
260
- cmd, workdir, req.limits, apply_rlimits=policy.apply_rlimits
303
+ cmd,
304
+ workdir,
305
+ req.limits,
306
+ apply_rlimits=policy.apply_rlimits,
307
+ env=biber_env,
261
308
  )
262
309
 
263
310
  log = truncate_log(out, req.limits)
@@ -21,7 +21,15 @@ import zipfile
21
21
  from pathlib import Path
22
22
  from typing import TYPE_CHECKING
23
23
 
24
- __all__ = ["BuildError", "ensure_tectonic", "run_makeindex", "run_tectonic"]
24
+ __all__ = [
25
+ "BuildError",
26
+ "biber_for_build",
27
+ "ensure_tectonic",
28
+ "env_with_biber",
29
+ "probe_bcf",
30
+ "run_makeindex",
31
+ "run_tectonic",
32
+ ]
25
33
 
26
34
  if TYPE_CHECKING:
27
35
  from .console import Console
@@ -373,7 +381,7 @@ def _ensure_biber(version: str, console: Console) -> Path:
373
381
  return cached
374
382
 
375
383
 
376
- def _biber_for_build(build_dir: Path, job: str, console: Console) -> Path | None:
384
+ def biber_for_build(build_dir: Path, job: str, console: Console) -> Path | None:
377
385
  """Return a correctly-versioned biber if the BCF file reveals a mismatch."""
378
386
  bcf = build_dir / f"{job}.bcf"
379
387
  if not bcf.exists():
@@ -400,13 +408,13 @@ def _biber_for_build(build_dir: Path, job: str, console: Console) -> Path | None
400
408
  return _ensure_biber(biber_ver, console)
401
409
 
402
410
 
403
- def _env_with_biber(biber: Path) -> dict[str, str]:
411
+ def env_with_biber(biber: Path) -> dict[str, str]:
404
412
  env = os.environ.copy()
405
413
  env["PATH"] = str(biber.parent) + os.pathsep + env.get("PATH", "")
406
414
  return env
407
415
 
408
416
 
409
- def _probe_bcf(cmd: list[str]) -> None:
417
+ def probe_bcf(cmd: list[str]) -> None:
410
418
  """Run tectonic with a no-op biber so the BCF file is written to build_dir.
411
419
 
412
420
  Tectonic cleans up intermediates when biber fails, so the BCF is never
@@ -419,7 +427,7 @@ def _probe_bcf(cmd: list[str]) -> None:
419
427
  fake = tmpdir / "biber"
420
428
  fake.write_text("#!/bin/sh\nexit 0\n")
421
429
  fake.chmod(0o755)
422
- subprocess.run(cmd, env=_env_with_biber(fake), capture_output=True)
430
+ subprocess.run(cmd, env=env_with_biber(fake), capture_output=True)
423
431
  finally:
424
432
  shutil.rmtree(tmpdir, ignore_errors=True)
425
433
 
@@ -453,12 +461,12 @@ def run_tectonic(
453
461
  # Determine the right biber from the BCF. If no BCF exists yet (first build
454
462
  # or after a clean), run a silent probe pass with a no-op biber so tectonic
455
463
  # writes the BCF to build_dir without actually needing biber installed.
456
- biber = _biber_for_build(build_dir, job, console)
464
+ biber = biber_for_build(build_dir, job, console)
457
465
  if biber is None:
458
- _probe_bcf(cmd)
459
- biber = _biber_for_build(build_dir, job, console)
466
+ probe_bcf(cmd)
467
+ biber = biber_for_build(build_dir, job, console)
460
468
 
461
- env = _env_with_biber(biber) if biber is not None else None
469
+ env = env_with_biber(biber) if biber is not None else None
462
470
  proc = subprocess.run(cmd, env=env)
463
471
  if proc.returncode != 0:
464
472
  log = build_dir / f"{tex_file.stem}.log"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pytex-preprocessor
3
- Version: 1.0.3
3
+ Version: 1.0.5
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