A high-level Python wrapper for Tectonic LaTeX compiler with project-aware compilation
Project description
B8TeX
A modern, type-safe Python wrapper for the Tectonic LaTeX compiler.
B8TeX provides a high-level, Pythonic interface for compiling LaTeX documents with support for:
- In-memory compilation from strings
- Custom style files and packages
- Multi-file projects with dependency tracking
- Type-safe API with comprehensive error handling
- Project-aware builds with multiple output formats
Features
- Template System (NEW in v0.2.0): Professional document templates (NeurIPS, ICLR, ACL) with programmatic Python API
- Data-Driven: Pandas DataFrames → LaTeX tables, Matplotlib → embedded figures
- Modern Python: Built for Python 3.13+ with full type hints
- Auto-Install: Automatically downloads the Tectonic binary on first use
- Flexible Sources: Compile from files, strings, or in-memory sources
- Custom Packages: Bundle custom
.sty,.cls, and resource files with your documents - Project Support: Multi-target builds with Tectonic V1 and V2 interface support
- Type-Safe: Comprehensive type hints and runtime validation
- Secure: Built-in security policies with LaTeX validation, resource limits, and configurable timeouts
- Resilient: Automatic retry with exponential backoff and graceful failure handling
- Rich Results: Structured compilation results with warnings, errors, and artifacts
- Cross-Platform: Supports Linux, macOS, and Windows (x86_64 and aarch64)
Security & Resource Management
B8TeX includes comprehensive security features to protect against malicious or resource-intensive LaTeX documents:
LaTeX Content Validation
Validates documents for security and resource constraints:
from b8tex.core.validation import ContentValidator, ValidationLimits
# Configure validation limits
limits = ValidationLimits(
max_file_size=10 * 1024 * 1024, # 10MB per file
max_total_size=50 * 1024 * 1024, # 50MB total
max_files=100, # Max 100 files
allow_shell_escape=False, # Block shell-escape packages
dangerous_packages={"shellesc", "minted", "pythontex"} # Block dangerous packages
)
validator = ContentValidator(limits=limits, mode="strict")
errors = validator.validate_document(document)
Validation modes:
strict: Raise errors immediately on validation failurepermissive: Collect warnings, only error on critical issues (default)disabled: Skip validation entirely
Resource Limits
Control memory, CPU, and output size limits:
from b8tex.core.runner import ResourceLimits, ProcessRunner
limits = ResourceLimits(
memory_mb=1024, # 1GB memory limit
timeout_seconds=300.0, # 5 minute timeout
max_output_size_mb=100, # Max 100MB output
cpu_time_seconds=300 # Max 5 minutes CPU time (Unix only)
)
runner = ProcessRunner(limits=limits)
Configurable Timeouts
Set different timeouts for different operations with document size-based scaling:
from b8tex.core.timeouts import TimeoutConfig
timeouts = TimeoutConfig(
compilation=600.0, # 10 minute compilation timeout
download=300.0, # 5 minute download timeout
probe=5.0, # 5 second probe timeout
global_multiplier=2.0, # Double all timeouts for slow systems
enable_size_scaling=True, # Scale timeout by document size
size_scale_factor=0.1 # +0.1s per KB above base size
)
# Get effective timeout for a 500KB document
timeout = timeouts.get("compilation", document_size_kb=500) # 640s (600 + 400*0.1)
Retry Policies & Graceful Failure
Handle transient failures with automatic retry and exponential backoff:
from b8tex.core.retry import RetryPolicy, retry_with_policy
policy = RetryPolicy(
max_attempts=3,
initial_delay=1.0,
max_delay=30.0,
exponential_backoff=True,
backoff_factor=2.0,
retryable_exceptions=(OSError, IOError, TimeoutError)
)
result = retry_with_policy(
policy,
lambda: compile_document(doc),
operation_name="compilation"
)
Security Configuration
All security settings can be configured via ~/.config/b8tex/config.toml:
# Cache limits
max_cache_size_mb = 1000
max_cache_age_days = 30
[validation]
mode = "permissive" # "strict", "permissive", or "disabled"
[validation.limits]
max_file_size = 10485760 # 10MB
max_total_size = 52428800 # 50MB
max_files = 100
allow_shell_escape = false # Block minted, pythontex, etc.
[resource_limits]
memory_mb = 1024
timeout_seconds = 300.0
max_output_size_mb = 100
[timeouts]
compilation = 300.0
download = 300.0
global_multiplier = 1.0
enable_size_scaling = true
Or via environment variables:
# Validation
export B8TEX_VALIDATION_MODE=strict
# Resource limits
export B8TEX_MEMORY_LIMIT_MB=2048
export B8TEX_CPU_TIME_LIMIT=600
# Timeouts
export B8TEX_TIMEOUT_COMPILATION=600
export B8TEX_TIMEOUT_MULTIPLIER=2.0
# Cache
export B8TEX_MAX_CACHE_SIZE_MB=2000
Installation
Simple Installation (Recommended)
B8TeX will automatically download and install the Tectonic binary on first use:
# Using uv (recommended)
uv pip install b8tex
# Or with pip
pip install b8tex
That's it! When you first use B8TeX, it will automatically download the appropriate Tectonic binary for your platform.
Manual Tectonic Installation (Optional)
If you prefer to manage Tectonic yourself:
# macOS
brew install tectonic
# Linux (see https://tectonic-typesetting.github.io/install.html)
# Windows (see https://tectonic-typesetting.github.io/install.html)
Or use the B8TeX CLI to manually install:
# Download and install Tectonic binary
b8tex install-binary
# Download a specific version
b8tex install-binary --version=0.15.0
# Force re-download
b8tex install-binary --force
Configuration
B8TeX can be configured via environment variables or a configuration file.
Environment Variables
# Disable automatic downloads
export B8TEX_NO_AUTO_DOWNLOAD=1
# Specify Tectonic version
export B8TEX_TECTONIC_VERSION=0.15.0
# Use custom Tectonic binary
export TECTONIC_PATH=/usr/local/bin/tectonic
# Override config file location
export B8TEX_CONFIG_PATH=~/.config/b8tex/config.toml
Configuration File
Create a configuration file at ~/.config/b8tex/config.toml (or use b8tex init-config):
# Binary management
auto_download = true
# tectonic_version = "0.15.0"
# binary_path = "/usr/local/bin/tectonic"
# Cache configuration
max_cache_size_mb = 1000
max_cache_age_days = 30
# Security and validation
[validation]
mode = "permissive" # "strict", "permissive", or "disabled"
[validation.limits]
max_file_size = 10485760 # 10MB
max_total_size = 52428800 # 50MB
max_files = 100
allow_shell_escape = false
# Resource limits
[resource_limits]
memory_mb = 1024
timeout_seconds = 300.0
max_output_size_mb = 100
# Timeouts
[timeouts]
compilation = 300.0
download = 300.0
global_multiplier = 1.0
enable_size_scaling = true
CLI Commands
# Initialize default config file
b8tex init-config
# Show current configuration
b8tex config
# Install Tectonic binary
b8tex install-binary
# Show help
b8tex help
For Development
git clone https://github.com/samehkamaleldin/pytex.git
cd b8tex
uv venv
source .venv/bin/activate # or `.venv\Scripts\activate` on Windows
uv pip install -e ".[dev]"
Quick Start
Basic Usage
Compile LaTeX from a string:
from b8tex import compile_string
latex = r"""
\documentclass{article}
\title{Hello B8TeX}
\begin{document}
\maketitle
Hello, world!
\end{document}
"""
result = compile_string(latex)
if result.success:
print(f"PDF generated: {result.pdf_path}")
Custom Style Files
Use custom packages and styles:
from b8tex import Document, InMemorySource, Resource, compile_document
# Define a custom style
style = InMemorySource("mystyle.sty", r"""
\ProvidesPackage{mystyle}
\newcommand{\mycommand}{Custom text}
""")
# Main document
doc = Document(
name="custom",
entrypoint=InMemorySource("main.tex", r"""
\documentclass{article}
\usepackage{mystyle}
\begin{document}
\mycommand
\end{document}
"""),
resources=[Resource(style)]
)
result = compile_document(doc)
Project Builds
Compile multi-target projects:
from b8tex import Project, Target, Document, BuildOptions, OutputFormat
project = Project.from_directory(Path("my_project"))
project.add_target(Target(
name="pdf",
document=Document.from_path(Path("main.tex")),
options=BuildOptions(outfmt=OutputFormat.PDF)
))
from b8tex import TectonicCompiler
compiler = TectonicCompiler()
result = compiler.build_project(project, target="pdf")
Template System
New in v0.2.0: B8TeX now includes a complete template system for programmatic document generation. Write Python code instead of LaTeX preambles!
Why Templates?
- No LaTeX Boilerplate: Focus on content, not preambles
- Type-Safe: Full type hints and autocomplete
- Professional Styles: NeurIPS, ICLR, ACL conference templates
- Data-Driven: Pandas DataFrames → tables, Matplotlib → figures
- Validation: Catch errors before compilation
Quick Example
from b8tex.templates import compile_template, Author, Section
result = compile_template(
template="neurips",
title="My Research Paper",
authors=[
Author(name="Alice", affiliation="MIT"),
Author(name="Bob", affiliation="Stanford"),
],
abstract="This paper presents a novel approach...",
sections=[
Section(title="Introduction", content="Our work addresses..."),
Section(title="Methods", content="We propose..."),
Section(title="Results", content="Experiments show..."),
],
status="draft", # or "final", "confidential"
)
print(f"PDF: {result.pdf_path}")
Template Features
1. Simple API for Quick Documents
from b8tex.templates import compile_template
# One-liner compilation
result = compile_template(
template="neurips",
title="Quick Paper",
authors="John Doe",
content=r"\section{Intro} Hello world!",
)
2. Fluent Builder with Context Managers
from b8tex.templates import DocumentBuilder
with DocumentBuilder(template="neurips", title="My Paper") as builder:
builder.add_author("Alice", affiliation="MIT")
builder.set_abstract("This paper presents...")
with builder.section("Introduction"):
builder.add_text("Our motivation is...")
with builder.subsection("Background"):
builder.add_text("Previous work includes...")
doc = builder.build()
result = b8tex.compile_template_document(doc)
3. Data-Driven Documents
Requires optional dependencies: pip install b8tex[templates]
from b8tex.templates import DocumentBuilder
import pandas as pd
import matplotlib.pyplot as plt
with DocumentBuilder(template="neurips", title="Results Report") as builder:
# Pandas DataFrame → LaTeX table
df = pd.DataFrame({
"Model": ["ResNet-18", "ResNet-34", "ResNet-50"],
"Accuracy": [94.2, 95.8, 96.5],
"Params (M)": [11.7, 21.8, 25.6],
})
with builder.section("Results"):
builder.add_table(
df,
caption="Model comparison",
label="tab:results"
)
# Matplotlib figure → embedded PDF
fig, ax = plt.subplots()
ax.plot([1, 2, 3], [94, 95, 96])
ax.set_title("Accuracy over time")
builder.add_plot(
fig,
caption="Training progress",
label="fig:training"
)
doc = builder.build()
result = b8tex.compile_template_document(doc)
Available Templates
- NeurIPS: Conference on Neural Information Processing Systems
- ICLR: International Conference on Learning Representations
- ACL: Association for Computational Linguistics
Status Modes
final: Clean, publication-ready outputdraft: Line numbers, watermarks, TODOs highlightedconfidential: Watermark for internal documentsinternal: Organization-specific formatting
Template Variables (Jinja2)
from b8tex.templates import compile_template
result = compile_template(
template="neurips",
title="Experiment Results",
content="Accuracy: {{ accuracy }}% on {{ dataset }}",
template_variables={
"accuracy": 95.5,
"dataset": "CIFAR-10",
},
)
Validation
from b8tex.templates import validate_template_document
# Validate before compilation
is_valid, issues = validate_template_document(doc)
if not is_valid:
for issue in issues:
print(f"[{issue.severity}] {issue.message}")
More Examples
See examples/ directory:
08_template_basic.py- Simple template usage09_neurips_template.py- Full-featured paper with nested sections10_data_driven_template.py- Tables and plots from data
Documentation
For more examples, see the examples/ directory.
Core Concepts
- Document: A LaTeX document with entrypoint and resources
- Resource: Style files, images, bibliographies, etc.
- BuildOptions: Compilation settings (output format, reruns, security, etc.)
- CompileResult: Structured result with artifacts, warnings, and errors
- Project: Multi-target build configuration
API Reference
Main Functions
compile_string(content, **options)- Compile LaTeX source codecompile_document(document, **options)- Compile a Document objectTectonicCompiler()- Main compiler interface for advanced usage
Core Classes
Document- LaTeX document representationInMemorySource- In-memory file contentResource- Additional files (styles, images, etc.)BuildOptions- Compilation configurationProject- Multi-target projectCompileResult- Compilation results
Development
Setup
# Clone and install
git clone https://github.com/samehkamaleldin/pytex.git
cd b8tex
uv venv
source .venv/bin/activate
uv pip install -e ".[dev]"
Running Tests
# Run all tests
pytest
# Run with coverage
pytest --cov=b8tex --cov-report=html
# Run type checking
mypy src/b8tex
# Run linting
ruff check src tests
Code Quality
This project uses:
- mypy for static type checking
- ruff for linting and formatting
- pytest for testing
All code must pass type checking and linting before merging.
Requirements
- Python 3.13+
- Tectonic LaTeX compiler (automatically downloaded if not found)
- uv (recommended) or pip
Supported Platforms
B8TeX can automatically download Tectonic for the following platforms:
- Linux: x86_64, aarch64
- macOS: x86_64 (Intel), aarch64 (Apple Silicon)
- Windows: x86_64
If automatic download is not available for your platform, you can install Tectonic manually following the official installation guide.
How Auto-Download Works
When you first use B8TeX:
-
B8TeX checks for Tectonic in the following order:
- Custom binary path from config file
TECTONIC_PATHenvironment variable- System PATH
- B8TeX cache directory (
~/.cache/b8tex/or platform equivalent)
-
If Tectonic is not found and auto-download is enabled:
- Downloads the appropriate binary from Tectonic GitHub releases
- Extracts and caches it in
~/.cache/b8tex/ - Uses the cached binary for all future compilations
-
If auto-download is disabled or fails:
- Provides clear instructions for manual installation
- Suggests using
b8tex install-binarycommand
You can disable auto-download by:
- Setting
B8TEX_NO_AUTO_DOWNLOAD=1environment variable - Setting
auto_download = falsein~/.config/b8tex/config.toml
Troubleshooting
Common Issues
Binary Not Found
Problem: MissingBinaryError: Tectonic binary not found
Solutions:
- Enable auto-download (default): B8TeX will automatically download Tectonic
- Manual installation:
b8tex install-binary - Install Tectonic system-wide:
brew install tectonic(macOS) or follow official guide - Set custom path:
export TECTONIC_PATH=/path/to/tectonic
Compilation Fails
Problem: Compilation returns errors but unclear why
Solutions:
- Check
result.errorsfor specific error messages - Use
options.print_log=Trueto see full Tectonic output - Enable
options.keep_logs=Trueto preserve log files - Verify LaTeX syntax is valid (try with
tectoniccommand directly)
Permission Errors
Problem: PermissionError when compiling
Solutions:
- Check write permissions in output directory
- Verify cache directory is writable:
~/.cache/b8tex/ - Try running with elevated permissions (not recommended)
- Use custom output directory:
workdir=Path("/tmp/b8tex")
Platform Not Supported
Problem: UnsupportedPlatformError on auto-download
Solutions:
- Install Tectonic manually for your platform
- Set
TECTONIC_PATHto your manual installation - Open an issue if you think your platform should be supported
Slow Compilation
Problem: Compilation is slower than expected
Solutions:
- Enable caching (on by default):
TectonicCompiler(use_cache=True) - Use
only_cached=Truefor offline/fast builds - Reduce LaTeX reruns:
BuildOptions(reruns=1) - Check if cache is being invalidated unnecessarily
- See Performance Tips section below
Cache Issues
Problem: Cache not working or corrupted
Solutions:
- Clear cache:
b8tex clean-cacheorcache.clear() - Check cache stats:
cache.stats() - Verify cache directory exists:
~/.cache/b8tex/build_cache.db - Clean old entries:
cache.cleanup(max_age_days=30)
FAQ
General
Q: Do I need to install LaTeX? A: No! B8TeX uses Tectonic, which is a self-contained LaTeX distribution. You don't need TeX Live, MiKTeX, or any other LaTeX distribution.
Q: What Python versions are supported? A: B8TeX requires Python 3.13 or later for modern type hints and language features.
Q: Is B8TeX production-ready? A: B8TeX is currently in active development. The core API is stable, but advanced features are still being added. Check the version number and release notes.
Q: How does B8TeX compare to other LaTeX wrappers? A: B8TeX focuses on modern Python features, type safety, and developer experience. It's built specifically for Tectonic and offers features like automatic binary management, build caching, and in-memory compilation.
Usage
Q: Can I compile LaTeX files from disk?
A: Yes! Use Document.from_path(Path("document.tex")) or pass a Path object directly.
Q: How do I include images in my document? A: Add images as resources:
from b8tex import Document, Resource
doc = Document(
entrypoint=Path("main.tex"),
resources=[Resource(Path("image.png"))]
)
Q: Can I compile multiple documents in parallel?
A: Yes! Use the async API or Python's concurrent.futures. See examples/04_async_compilation.py.
Q: How do I use custom LaTeX packages?
A: Add them as resources with kind="sty":
Resource(Path("mypackage.sty"), kind="sty")
Q: Can I compile without internet access?
A: Yes! Once Tectonic is installed and packages are cached, use BuildOptions(only_cached=True).
Configuration
Q: Where is the cache directory? A: Platform-dependent:
- macOS:
~/Library/Caches/b8tex/ - Linux:
~/.cache/b8tex/ - Windows:
%LOCALAPPDATA%\b8tex\cache\
Q: How do I disable auto-download?
A: Set B8TEX_NO_AUTO_DOWNLOAD=1 or auto_download = false in config file.
Q: Can I use a specific Tectonic version? A: Yes! Set in config file:
tectonic_version = "0.15.0"
Or use: b8tex install-binary --version=0.15.0
Q: How do I change the output directory?
A: Use the workdir parameter:
result = compiler.compile_document(doc, workdir=Path("/tmp/output"))
Performance
Q: Why is the first compilation slow? A: Tectonic downloads required LaTeX packages on first use. Subsequent compilations are much faster thanks to package caching.
Q: Does B8TeX cache compilation results?
A: Yes! Build caching is enabled by default. See examples/05_cache_demo.py.
Q: How much space does the cache use?
A: Check with cache.stats(). Typically 10-100MB depending on usage. Clean old entries with cache.cleanup().
Performance Tips
1. Enable Build Caching (Default)
Build caching can speed up repeated compilations by 10-100x:
from b8tex import TectonicCompiler, BuildCache
# Caching is enabled by default
compiler = TectonicCompiler(use_cache=True)
# Or explicitly create cache
cache = BuildCache()
compiler = TectonicCompiler(cache=cache)
When cache helps:
- Repeated compilation of unchanged documents
- CI/CD pipelines
- Development with frequent rebuilds
- Batch processing similar documents
Cache invalidation: The cache automatically invalidates when:
- Document content changes
- Build options change
- Tectonic version changes
- Dependencies are modified
2. Use Only-Cached Mode
For fastest compilation when all packages are already downloaded:
from b8tex import BuildOptions
options = BuildOptions(only_cached=True)
result = compiler.compile_document(doc, options=options)
This prevents Tectonic from downloading packages, ideal for offline work or CI.
3. Reduce Reruns
LaTeX sometimes needs multiple passes. Limit reruns for faster compilation:
options = BuildOptions(reruns=1) # Default is automatic
Trade-off: Cross-references and bibliographies may not resolve correctly.
4. Parallel Compilation
For batch processing, use async or parallel execution:
from concurrent.futures import ThreadPoolExecutor
def compile_doc(content):
return compile_string(content)
with ThreadPoolExecutor(max_workers=4) as executor:
results = list(executor.map(compile_doc, documents))
Speedup: Near-linear with number of cores for independent documents.
5. Persistent Workspaces
Reuse workspace directories to avoid file I/O:
workdir = Path("/tmp/b8tex_workspace")
workdir.mkdir(exist_ok=True)
for doc in documents:
result = compiler.compile_document(doc, workdir=workdir)
6. In-Memory Compilation
Use in-memory sources instead of file I/O when possible:
from b8tex import InMemorySource, Document
doc = Document(
entrypoint=InMemorySource("doc.tex", latex_content),
resources=[InMemorySource("style.sty", style_content)]
)
Benefit: Faster file operations, no disk cleanup needed.
7. Cache Management
Keep cache healthy with periodic cleanup:
# Remove entries older than 30 days
cache.cleanup(max_age_days=30)
# Check cache size
stats = cache.stats()
print(f"Cache entries: {stats['total_entries']}")
print(f"Cache size: {stats['db_size_bytes'] / 1024 / 1024:.1f} MB")
# Clear if needed
if stats['db_size_bytes'] > 100_000_000: # > 100 MB
cache.clear()
8. Minimize Document Changes
The cache fingerprint includes all content. To maximize cache hits:
- Use variables/templates for changing content
- Separate frequently-changing content from static boilerplate
- Consider using LaTeX's
\inputfor modular documents
Performance Benchmarks
Typical compilation times on modern hardware:
| Scenario | First Compile | Cached | Speedup |
|---|---|---|---|
| Simple document (1 page) | 2-5s | 0.1-0.5s | 10-20x |
| Medium document (10 pages) | 5-15s | 0.2-1s | 15-25x |
| Large document (100 pages) | 15-60s | 1-3s | 15-30x |
| With images/complex math | 10-30s | 0.5-2s | 15-20x |
Note: First compile includes package downloads. Subsequent first compiles (different documents) are faster as packages are cached by Tectonic.
License
MIT License - see LICENSE file for details.
Contributing
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch
- Add tests for new features
- Ensure all tests pass and types check
- Submit a pull request
Acknowledgments
- Built on top of the excellent Tectonic LaTeX compiler
- Inspired by modern Python packaging and type safety best practices
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file b8tex-0.2.1.tar.gz.
File metadata
- Download URL: b8tex-0.2.1.tar.gz
- Upload date:
- Size: 184.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.7.21
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5879facba927d22437efaaa1ac680e1dda65dcd4c17f9c58ca3fc5a3d22327a3
|
|
| MD5 |
d006d262c3fb1d6a016fef2c86a76c35
|
|
| BLAKE2b-256 |
387a26ff4d959ba1aad6373e09c970417d1a4e64a54fab1e418534c8d5463481
|
File details
Details for the file b8tex-0.2.1-py3-none-any.whl.
File metadata
- Download URL: b8tex-0.2.1-py3-none-any.whl
- Upload date:
- Size: 97.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.7.21
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
68fd87417251d3a22396bfce1e4ae3d5daaa0c3ff03bd57327428a9028bec392
|
|
| MD5 |
81b89d192485f3589ef55f5b76503e8e
|
|
| BLAKE2b-256 |
529e9a71504e791d55d6518059e6d13cc66a4ec8f5366afdc7ec6b543b0fc4e5
|