Skip to main content

syntect-py

High-quality syntax highlighting for Python using Sublime Text grammars, powered by the syntect Rust crate. syntect-py 5.3.1 · PyO3 0.29 · Python ≥ 3.9 · Pure Rust regex (no C dependencies)

PyPI version PyPI Python versions PyPI wheel PyPI format License Tests PyO3


Features

  • 190+ built-in syntaxes — Rust, Python, JavaScript, TypeScript, Go, C, C++, Java, HTML, CSS, YAML, TOML, Markdown, and more
  • 55 bundled themes — Dracula, Nord, Monokai Extended, OneDark, Solarized, and more, loaded automatically
  • Syntax lookup primitives — token/alias lookup and first-line detection for shebangs, modelines, and XML
  • Theme authoring — construct themes with ThemeSettings, ThemeItem, ScopeSelectors, and StyleModifier
  • VS Code theme support — parse JSON/JSONC themes and register custom plist/VS Code themes
  • Assets compatibility APIAssets/HighlightingAssets fallback API for future expanded grammar data
  • Multiple output formats — inline HTML, class-based HTML, ANSI terminal escapes (24-bit color), LaTeX
  • Stateful highlighting — incremental re-highlighting with save/restore for editor integration
  • Real parse state — context-sensitive parsing across lines with scope stack introspection
  • Metadata access.tmPreferences metadata (indent rules, comment patterns, shell variables)
  • Serialization — dump/load syntax sets and themes as binary .packdump/.themedump files
  • Zero C dependencies — uses regex-fancy (pure Rust), no Oniguruma
  • Full type stubs — comprehensive .pyi stubs for IDE autocomplete and type checking
  • 360 tests — covering syntax/theme parity, JSONC themes, CSS selectors, bundled themes, assets fallback, stub conformance, and golden outputs

Installation

pip install syntect-py

From source

# From the repository root
python -m pip install maturin
maturin build --release --out pyext/dist -m pyext/Cargo.toml
python -m pip install --force-reinstall pyext/dist/*.whl

Prebuilt wheels are published for Linux x86_64/ARM64, macOS ARM64, and Windows x86_64. Source distributions are also published to PyPI from v* Git tags.


Quick Start

import syntect

# Load syntax definitions and themes (once at startup)
ss = syntect.SyntaxSet.load_defaults(True)
ts = syntect.ThemeSet.load_defaults()

# Get a syntax and theme
rust = ss.find_syntax_by_name("Rust")
theme = ts.get_theme("base16-ocean.dark")

# Create a highlighter
hl = syntect.Highlighter(rust, theme)

# Highlight a single line
tokens = hl.highlight_line("fn main() {}", ss, ts)
for style, text in tokens:
    print(f"{style.foreground.to_hex()} {text}")

# Or use the high-level convenience function
result = syntect.highlight_string(
    code='fn main() { println!("Hello"); }',
    syntax="Rust",
    theme="base16-ocean.dark",
    syntax_set=ss,
    theme_set=ts
)

print(result.html)          # HTML output
print(result.as_terminal_escaped(True))  # ANSI terminal output
print(result.as_latex_escaped())          # LaTeX output

Incremental Highlighting

For editors that need to re-highlight after changes:

hl = syntect.Highlighter(rust, theme)
state = hl.save_state(ss, ts)  # Save highlighting state

# ... some time later ...
hl2 = syntect.Highlighter.from_state(state, theme)  # Resume from state

# Or use the stateful HighlightLines API (upstream behavior)
hl_lines = syntect.HighlightLines(rust, ss, ts, "base16-ocean.dark")
for line in code.split("\n"):
    tokens = hl_lines.highlight_line(line, ss)  # 2 args: (line, ss)

# Or construct directly from a Theme object:
hl_direct = syntect.HighlightLines.with_theme(rust, ss, theme)

HTML Output

Inline HTML (single tokens)

tokens = hl.highlight_line("fn main() {}", ss, ts)
html = syntect.as_html(tokens, "if_different", None)
# <span style="color:#B48EAD;">fn</span><span style="color:#C0C5CE;"> </span>...

Class-based HTML (compact, CSS-externally styled)

class_style = syntect.ClassStyle.spaced_prefixed("syn-")
html = syntect.tokens_to_classed_spans(tokens, class_style)
# <span class="syn-keyword">fn</span><span class="syn-punctuation"> </span>...

Streaming HTML generator

gen = syntect.ClassedHTMLGenerator(rust, ss, class_style)
for line in code.split("\n"):
    gen.parse_html_for_line_which_includes_newline(line)
html = gen.finalize()  # Closes any open spans

Full HTML with line numbers

html = syntect.highlighted_html_at_line_and_column_number(
    code, rust, theme, ss, ts, start_line=1
)
# <pre><span data-line="1">...</span>\n<span data-line="2">...</span></pre>

CSS generation

css = syntect.css_for_theme(theme, "spaced")
# .keyword { color: #B48EAD; }
# .function { color: #8FA1B3; }

cs = syntect.ClassStyle.spaced_prefixed("syn-")
css = syntect.css_for_theme_class(theme, cs)

CSS generation preserves comma-separated selectors and emits combined font styles such as bold + italic + underline.


Theme Authoring and VS Code Themes

settings = syntect.ThemeSettings()
settings.background = syntect.Color.from_hex("#1E1E1E")

item = syntect.ThemeItem(
    syntect.ScopeSelectors.from_string("variable, constant"),
    syntect.StyleModifier(
        foreground=syntect.Color.from_hex("#FFD700"),
        font_style=syntect.FontStyle.from_string("bold italic underline"),
    ),
)
theme = syntect.Theme(
    name="Custom Theme", author="me", settings=settings, scopes=[item]
)

ts = syntect.ThemeSet()
ts.add_theme("custom", theme)

# VS Code JSON/JSONC or plist XML
content = open("theme.json", encoding="utf-8").read()
syntect.add_custom_theme("vscode-theme", content)
print(syntect.list_themes())

The process-wide add_custom_theme() registry is separate from caller-owned ThemeSet instances. Use ThemeSet.add_theme() for an isolated set.

Assets Compatibility API

assets = syntect.Assets.from_binary()
assets.set_fallback_theme("Monokai Extended")
asset_syntaxes = assets.get_syntax_set()
asset_themes = assets.get_theme_set()
theme = assets.get_theme("unknown-theme")  # configured fallback

The API is ready for expanded bat grammar data. The current implementation uses fork-compatible default syntax/theme data because the published syntect-assets binary dump is not compatible with this fork's serialization.

Terminal Output (24-bit color)

tokens = hl.highlight_line("fn main() {}", ss, ts)
escaped = syntect.as_terminal_escaped(tokens, include_bg=True)
print(escaped, end="")  # \x1b[38;2;180;142;173mfn\x1b[38;2;192;197;206m ...

Alpha transparency is handled automatically — foreground colors are blended with the background.


LaTeX Output

tokens = hl.highlight_line("fn main() {}", ss, ts)
latex = syntect.as_latex_escaped(tokens)
# \textcolor[RGB]{180,142,173}{fn}\textcolor[RGB]{192;197;206}{ }...

Spaces and newlines are elided when the style doesn't change.


Metadata Access (.tmPreferences)

ss = syntect.SyntaxSet.load_defaults(True)
meta = ss.metadata
if meta:
    for mset in meta.sets:
        print(mset.selector_string)       # "source.python"
        item = mset.items
        print(item.line_comment)          # "//"
        print(item.indent_parens)         # True/False
        print(item.shell_variables)       # List[Tuple[str, str]]
        print(item.increase_indent_pattern)

Parsing Introspection

ps = syntect.ParseState("Rust", ss)
output = ps.parse_line("fn main() {", ss)

for pos, op in output.ops:
    print(f"  {pos}: {op}")
    # 0: Push(source.rust)
    # 1: Push(keyword.declaration)

is_speculative = ps.is_speculative()  # True during backtracking
print(ps.syntax_name)                 # "Rust"

Dump / Serialization

# Save to binary dump (fast loading)
syntect.dump_syntax_set(ss, "syntaxes.packdump")
syntect.dump_theme_set(ts, "themes.themedump")

# Load from dump
ss = syntect.load_syntax_set("syntaxes.packdump")
ts = syntect.load_theme_set("themes.themedump")

Utility Functions

# Split tokens at a character position
left, right = syntect.split_at(tokens, 5)

# Modify style in a character range
modified = syntect.modify_range(tokens, 0, 5, new_style)

# Iterate lines with their endings
for line, ending in syntect.lines_with_endings("hello\nworld\r\n"):
    print(repr(line), repr(ending))
    # 'hello' '\n'  'world' '\r\n'

Error Handling

import syntect

try:
    ss = syntect.SyntaxSet.load_from_folder("/missing", True)
except (syntect.LoadingError, OSError) as e:
    print(f"Load error: {e}")

theme = ts.get_theme("nonexistent")
if theme is None:  # Returns None when no fallback is supplied
    print("Theme not found")

fallback = ts.get_theme("nonexistent", "base16-ocean.dark")

Exception types: LoadingError, ParsingError, DumpError, ParseSyntaxError, ValueError, OSError, RuntimeError, IndexError


API Gotchas

Issue Solution
Highlighter.highlight_line() takes 3 args (line, ss, ts) HighlightLines.highlight_line() takes 2 args (line, ss)
as_html() requires default_bg parameter Pass None for no default
rust.variables is List[Tuple[str, str]] Not a Dict[str, str]
MatchPower.value is a property Not a method: use mp.value, not mp.value()
save_state() requires ss, ts arguments Not no-arg: hl.save_state(ss, ts)
get_theme() returns None for missing Pass fallback="theme-name" for fallback lookup
HighlightLines unknown theme Falls back to InspiredGitHub when available
add_custom_theme() registry Process-wide; use ThemeSet.add_theme() for an isolated set
Color.to_hex() returns uppercase #FF0000, not #ff0000
is_prefix_of() semantics parent.is_prefix_of(child) — parent checks if child starts with it

Project Structure

syntect-py/
├── ARCHITECTURE.md       # Architecture documentation
├── QUICKREF.md           # Quick reference guide
├── CHANGELOG.md          # Version history
├── DESIGN.md             # Design decisions
├── Readme.md             # This file
├── .github/workflows/CI.yml      # Multiplatform CI
├── .github/workflows/Release.yml # Wheel/sdist build and PyPI publish
├── pyext/
│   ├── Cargo.toml        # PyO3 + syntect dependencies
│   ├── pyproject.toml    # maturin build configuration and theme inclusion
│   ├── README.md         # Python package metadata readme
│   ├── syntect.pyi       # Type stubs (complete)
│   ├── syntect/
│   │   ├── __init__.py   # Mixed-package wrapper
│   │   ├── __init__.pyi
│   │   └── themes/       # 55 bundled mordant themes
│   ├── src/
│   │   ├── lib.rs        # Module entry point
│   │   ├── style.rs      # Color, FontStyle, Style, StyleModifier
│   │   ├── syntax_set.rs # SyntaxSet, SyntaxReference, SyntaxSetBuilder
│   │   ├── theme_set.rs  # ThemeSet, Theme, ThemeSettings, ThemeItem, ScopeSelectors
│   │   ├── vscode_theme.rs# VS Code JSON/JSONC conversion and registry
│   │   ├── assets.rs      # Assets/HighlightingAssets compatibility API
│   │   ├── metadata.rs   # Metadata, MetadataSet, MetadataItem
│   │   ├── highlighter.rs# Highlighter, HighlightState, HighlightLines
│   │   ├── highlighting.rs# ScoredStyle, ScopeRangeIterator
│   │   ├── parse_state.rs# ParseState, Scope, ScopeStack, etc.
│   │   ├── html.rs       # ClassedHTMLGenerator, CSS/HTML functions
│   │   ├── util.rs       # LinesWithEndings, split_at, modify_range
│   │   ├── convenience.rs# HighlightResult
│   │   ├── dumps.rs      # dump/load syntax/theme sets
│   │   ├── converters.rs # Py↔Rust conversion helpers
│   │   └── errors.rs     # Exception types
│   ├── examples/         # 9 example scripts
│   ├── benches/          # Benchmark scripts (highlighting, loading, parsing)
│   └── tests/            # Python, parity, stub, and golden-output tests

Examples

Example Description
basic_highlight.py Single-line highlighting with all output formats
incremental.py Stateful highlighting with save/restore
highlight_file.py Multi-line file highlighting with CRLF support
advanced_highlight.py Classed HTML, scope stack, split/modify
highlight_html.py Full HTML file generation with CSS
css_generator.py CSS generation for themes
benchmark.py Performance benchmarking
metadata_example.py Metadata access from .tmPreferences
error_handling.py Error handling patterns

Run any example from the repository root after installing the wheel:

python pyext/examples/basic_highlight.py

Documentation

Document Contents
ARCHITECTURE.md Architecture, module map, type mapping, design decisions
QUICKREF.md Complete API reference with examples and gotchas
syntect.pyi Type stubs for IDE autocomplete
docs/IMPROVEMENT_PLAN.md Remediation & improvement plan with phased execution

Tests

python -m pytest pyext/tests/ -v

346 tests passing. The suite includes stub conformance, JSONC theme conversion, CSS selector preservation, bundled-theme loading, assets fallback, LaTeX escaping, HTML/terminal output, and golden outputs.


Built On

syntect-py is a Python binding layer over the syntect Rust crate, which provides:

  • 190+ built-in syntax definitions from Sublime Text Packages
  • 55 bundled mordant themes plus syntect's built-in themes
  • JSON/JSONC VS Code theme conversion and custom theme registration
  • Pure Rust fancy-regex engine (no C dependencies)
  • 24-bit color ANSI terminal output, HTML, and LaTeX support

346 tests passing · syntect-py 5.3.1 · PyO3 0.29 · Python ≥ 3.9 · P1–P4.5 parity implemented · PyPI CI/release configured · expanded bat grammar data remains outstanding

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

syntect_py-5.3.1.tar.gz (4.1 MB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

syntect_py-5.3.1-cp39-abi3-win_amd64.whl (5.6 MB view details)

Uploaded CPython 3.9+Windows x86-64

syntect_py-5.3.1-cp39-abi3-manylinux_2_28_x86_64.whl (6.0 MB view details)

Uploaded CPython 3.9+manylinux: glibc 2.28+ x86-64

syntect_py-5.3.1-cp39-abi3-manylinux_2_28_aarch64.whl (6.0 MB view details)

Uploaded CPython 3.9+manylinux: glibc 2.28+ ARM64

syntect_py-5.3.1-cp39-abi3-macosx_11_0_arm64.whl (5.8 MB view details)

Uploaded CPython 3.9+macOS 11.0+ ARM64

File details

Details for the file syntect_py-5.3.1.tar.gz.

File metadata

  • Download URL: syntect_py-5.3.1.tar.gz
  • Upload date:
  • Size: 4.1 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for syntect_py-5.3.1.tar.gz
Algorithm Hash digest
SHA256 d637f76d824ccf335eb23fda36a127218a4f4fe6fd329803b63377db8c4b51b7
MD5 744644d4f55514be62c453311537f65c
BLAKE2b-256 f94084e55e4a61ecd2b4e758c4592819ee130d1d1da54231b74c8151340aa07c

See more details on using hashes here.

Provenance

The following attestation bundles were made for syntect_py-5.3.1.tar.gz:

Publisher: Release.yml on opticsWolf/syntect-py

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file syntect_py-5.3.1-cp39-abi3-win_amd64.whl.

File metadata

  • Download URL: syntect_py-5.3.1-cp39-abi3-win_amd64.whl
  • Upload date:
  • Size: 5.6 MB
  • Tags: CPython 3.9+, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for syntect_py-5.3.1-cp39-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 8177891a8258467d51844078b681af16274c936844b2ba3df8ee82c74067edfa
MD5 ac9666cacbe5d8d362de4b2584f0abb0
BLAKE2b-256 bd2813edeffd4c4e1fc064aa23f5d2115ba49fb3f57a7b1470eca57960ac6518

See more details on using hashes here.

Provenance

The following attestation bundles were made for syntect_py-5.3.1-cp39-abi3-win_amd64.whl:

Publisher: Release.yml on opticsWolf/syntect-py

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file syntect_py-5.3.1-cp39-abi3-manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for syntect_py-5.3.1-cp39-abi3-manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 4cb7e69629091a204ede5595494651b8892238c145b01f9a95c0450758869c6b
MD5 7fe477d95256efec442c121318408af0
BLAKE2b-256 c90ebe7bdbc6b94149d5a9b21a79dc02773aee5ced86f57ac4990f24fb42b720

See more details on using hashes here.

Provenance

The following attestation bundles were made for syntect_py-5.3.1-cp39-abi3-manylinux_2_28_x86_64.whl:

Publisher: Release.yml on opticsWolf/syntect-py

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file syntect_py-5.3.1-cp39-abi3-manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for syntect_py-5.3.1-cp39-abi3-manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 505785b8941c46d7cb777c12c782e0936861b09beb39e40f315210f88af6faed
MD5 372fc1ad2906672bb9333af5330d04e6
BLAKE2b-256 02ebaface7083ab31a93da6cf8845e33d2f53c23675254ff7cf58ff3386d9a36

See more details on using hashes here.

Provenance

The following attestation bundles were made for syntect_py-5.3.1-cp39-abi3-manylinux_2_28_aarch64.whl:

Publisher: Release.yml on opticsWolf/syntect-py

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file syntect_py-5.3.1-cp39-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for syntect_py-5.3.1-cp39-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 589615468cfc7a52e41b45a4b64234b42d6e7d2882941479a8a7db31321d8a01
MD5 ac1e4d66bb26c70e8a89b12be02a545f
BLAKE2b-256 1039b07358ca444526840a744436c8da5149d876e593f5852d87ff53f02a9059

See more details on using hashes here.

Provenance

The following attestation bundles were made for syntect_py-5.3.1-cp39-abi3-macosx_11_0_arm64.whl:

Publisher: Release.yml on opticsWolf/syntect-py

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

5.3.1 This release

5 files

5.3.0

5 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page