Switch from Pygments to Rosettes for faster, safer HTML syntax highlighting.
Prerequisites
- Python 3.14+
- Existing Pygments-based highlighting
- HTML output (Rosettes doesn't support LaTeX, RTF, etc.)
Step 1: Install Rosettes
pip install rosettes
Step 2: Update Imports
Before (Pygments):
from pygments import highlight
from pygments.lexers import get_lexer_by_name, PythonLexer
from pygments.formatters import HtmlFormatter
After (Rosettes):
from rosettes import highlight, supports_language
Step 3: Update Highlighting Calls
Before (Pygments):
def highlight_code(code: str, language: str) -> str:
try:
lexer = get_lexer_by_name(language)
except ClassNotFound:
lexer = TextLexer()
formatter = HtmlFormatter(cssclass="highlight")
return highlight(code, lexer, formatter)
After (Rosettes):
def highlight_code(code: str, language: str) -> str:
if not supports_language(language):
language = "plaintext"
return highlight(code, language, css_class_style="pygments")
Key differences:
- Single import instead of three
css_class_style="pygments"for theme compatibilitysupports_language()for validation
Step 4: Keep Your CSS
Your existing Pygments CSS theme works unchanged:
/* Your existing Pygments theme */
.highlight { background: #272822; }
.highlight .k { color: #66d9ef; }
.highlight .nf { color: #a6e22e; }
/* ... */
Rosettes generates the same CSS classes when usingcss_class_style="pygments".
Step 5: Update Line Highlighting (Optional)
Before (Pygments):
formatter = HtmlFormatter(
linenos=True,
hl_lines=[2, 3, 4],
)
After (Rosettes):
html = highlight(
code,
language,
show_linenos=True,
hl_lines={2, 3, 4}, # Note: set, not list
css_class_style="pygments",
)
Step 6: Update Parallel Highlighting (Optional)
Before (Pygments):
from concurrent.futures import ThreadPoolExecutor
def highlight_many_pygments(blocks):
with ThreadPoolExecutor(max_workers=4) as executor:
return list(executor.map(
lambda b: highlight_code(b[0], b[1]),
blocks
))
After (Rosettes):
from rosettes import highlight_many
def highlight_many_rosettes(blocks):
return highlight_many(blocks, css_class_style="pygments")
Built-in, optimized, and thread-safe.
Complete Migration Example
Before:
from pygments import highlight
from pygments.lexers import get_lexer_by_name, TextLexer
from pygments.lexers import ClassNotFound
from pygments.formatters import HtmlFormatter
class CodeHighlighter:
def __init__(self):
self.formatter = HtmlFormatter(cssclass="highlight")
def highlight(self, code: str, language: str) -> str:
try:
lexer = get_lexer_by_name(language)
except ClassNotFound:
lexer = TextLexer()
return highlight(code, lexer, self.formatter)
def highlight_with_lines(
self,
code: str,
language: str,
hl_lines: list[int],
) -> str:
try:
lexer = get_lexer_by_name(language)
except ClassNotFound:
lexer = TextLexer()
formatter = HtmlFormatter(
cssclass="highlight",
linenos=True,
hl_lines=hl_lines,
)
return highlight(code, lexer, formatter)
After:
from rosettes import highlight, highlight_many, supports_language
class CodeHighlighter:
def highlight(self, code: str, language: str) -> str:
if not supports_language(language):
language = "plaintext"
return highlight(code, language, css_class_style="pygments")
def highlight_with_lines(
self,
code: str,
language: str,
hl_lines: set[int],
) -> str:
if not supports_language(language):
language = "plaintext"
return highlight(
code,
language,
show_linenos=True,
hl_lines=hl_lines,
css_class_style="pygments",
)
def highlight_batch(
self,
blocks: list[tuple[str, str]],
) -> list[str]:
# Validate languages
validated = [
(code, lang if supports_language(lang) else "plaintext")
for code, lang in blocks
]
return highlight_many(validated, css_class_style="pygments")
API Mapping
| Pygments | Rosettes |
|---|---|
get_lexer_by_name(lang) |
get_lexer(lang) |
ClassNotFoundexception |
LookupErrorexception |
supports_language()check |
supports_language(lang) |
HtmlFormatter(cssclass=...) |
highlight(..., css_class=...) |
HtmlFormatter(linenos=True) |
highlight(..., show_linenos=True) |
hl_lines=[1,2,3] |
hl_lines={1,2,3}(set) |
Known Differences
| Aspect | Pygments | Rosettes |
|---|---|---|
| Languages | 500+ | 55 |
| Output formats | HTML, LaTeX, RTF, etc. | HTML only |
hl_linestype |
list |
setorfrozenset |
| Line number format | Configurable | Fixed format |
Verification
After migration, verify:
- Output looks the same: Compare rendered HTML
- CSS classes match: Inspect generated class names
- Performance improved: Benchmark before/after
- No errors: Test with all your languages
# Quick verification
from rosettes import highlight
html = highlight("def foo(): pass", "python", css_class_style="pygments")
assert '<span class="k">def</span>' in html
assert '<span class="nf">foo</span>' in html
print("✅ Migration successful!")
Next Steps
- Parallel Processing — Optimize batch highlighting
- Comparison — Full Rosettes vs Pygments comparison
- Pygments Themes — Theme compatibility details