Intelligent syntax highlighting and validation for Python template strings (PEP 750)
Project description
t-linter 🐍✨
Intelligent syntax highlighting and validation for Python template strings (PEP 750).
📣 💼 Maintainer update: Open to opportunities. 🔗 koxudaxi.dev
📖 Documentation
- 📦 Installation - VSCode Extension, PyPI, Build from source
- 🎨 VSCode Extension - Editor integration & setup
- 🔍 Check Command - CLI validation & output formats
- 🧹 Format Command - Canonical formatting for supported templates
- ⚙️ Configuration - pyproject.toml & ignore files
- 🌐 Supported Languages - HTML, T-HTML, SQL, JS, CSS, JSON, YAML, TOML
Overview
t-linter provides intelligent syntax highlighting and linting for Python template strings (PEP 750) through multiple distribution channels:
- 🔧 Command-line tool: Install via PyPI (
pip install t-linter) for direct CLI usage and LSP server - 🎨 VSCode Extension: Install from the Visual Studio Code Marketplace for seamless editor integration
Features
- 🎨 Smart Syntax Highlighting - Detects embedded languages in
t"..."strings - 🔍 Type-based Detection - Understands
Annotated[Template, "html"]annotations - 🚀 Fast - Built with Rust and Tree-sitter for optimal performance
- 🔧 Extensible - Support for HTML, T-HTML, SQL, JavaScript, CSS, JSON, YAML, TOML, and more
For HTML, T-HTML, JSON, YAML, and TOML, t-linter now splits responsibilities:
semanticTokens: Tree-sitter only, for low-latency highlightingcheck: strict parsing through thetstring-html,tstring-thtml,tstring-json,tstring-yaml, andtstring-tomlbackendsformatting: canonical formatting through the same Rust backends
Installation
Option 1: VSCode Extension (Recommended for VSCode users)
Step 1: Install the t-linter binary Install t-linter as a project dependency (recommended):
pip install t-linter
For better project isolation, add it to your project's requirements:
# Using pip with requirements.txt
echo "t-linter" >> requirements.txt
pip install -r requirements.txt
# Or using uv (recommended for faster installs)
uv add t-linter
Step 2: Install the VSCode extension Install the extension from the Visual Studio Code Marketplace:
- Open VSCode
- Go to Extensions (Ctrl+Shift+X / Cmd+Shift+X)
- Search for "t-linter"
- Click Install on "T-Linter - Python Template Strings Highlighter & Linter" by koxudaxi
Step 3: Disable Python Language Server To prevent conflicts with t-linter's syntax highlighting, disable the Python language server:
- Open VSCode Settings (Ctrl+, / Cmd+,)
- Search for "python.languageServer"
- Set it to "None"
Alternatively, add to your settings.json:
{
"python.languageServer": "None"
}
Step 4: Configure the server path (if needed) If t-linter is not in your PATH, configure the server path in VSCode settings:
-
Find your t-linter path by running in terminal:
which t-linter # macOS/Linux where t-linter # Windows
-
Open VSCode Settings (Ctrl+, / Cmd+,)
-
Search for "t-linter.serverPath"
-
Set the full path to your t-linter executable:
- Windows:
C:\Users\YourName\AppData\Local\Programs\Python\Python3xx\Scripts\t-linter.exe - macOS:
/Users/yourname/.local/bin/t-linteror/usr/local/bin/t-linter - Linux:
/home/yourname/.local/bin/t-linteror/usr/local/bin/t-linter
- Windows:
→ Install from VSCode Marketplace
Option 2: PyPI Package Only (CLI tool and LSP server)
For command-line usage or integration with other editors, install t-linter as a project dependency:
pip install t-linter
Or add to your project's dependencies:
# Using requirements.txt
t-linter
# Using uv
uv add t-linter
# Or manually in pyproject.toml
[project]
dependencies = [
"t-linter",
# other dependencies...
]
This provides the t-linter command-line tool and LSP server without the VSCode extension.
Option 3: Build from Source
For development or bleeding-edge features:
git clone https://github.com/koxudaxi/t-linter
cd t-linter
cargo install --path crates/t-linter
Usage
VSCode Extension
After installing both the PyPI package and VSCode extension, t-linter will automatically provide syntax highlighting for Python template strings.
Troubleshooting: If syntax highlighting doesn't work:
- Ensure
t-linteris installed: Runt-linter --versionin terminal - Check that Python language server is disabled:
python.languageServershould be set to "None" - Check the server path in VSCode settings:
t-linter.serverPath - Restart VSCode after making changes
Command Line Interface
If you installed via PyPI, you can use t-linter from the command line:
Run the language server (for editor integration):
t-linter lsp
In the LSP, diagnostics are debounced and published from the dedicated Rust
backends for HTML, T-HTML, JSON, YAML, and TOML templates. Formatting requests
rewrite the whole template literal using the backend formatter while keeping
interpolation source such as {name!r:>5} intact.
Check files for issues:
# Check Python files for template string issues
t-linter check file.py
t-linter check src/
# Output formats
t-linter check file.py --format json
t-linter check file.py --format github # GitHub Actions annotations
t-linter check file.py --error-on-issues # Exit with error code if issues found
check supports human, json, and github output formats.
check --format controls report output formatting only. Use the format
subcommand to rewrite supported template literals in place:
# Format Python files containing HTML/T-HTML/JSON/YAML/TOML templates
t-linter format file.py
t-linter format src/
# Check whether formatting would change any files
t-linter format --check file.py
# Override the formatter line length
t-linter format --line-length 100 file.py
# Format stdin
cat file.py | t-linter format --stdin-filename file.py -
Configuration can be provided via pyproject.toml:
[tool.t-linter]
line-length = 80
extend-exclude = ["generated", "vendor"]
ignore-file = ".t-linterignore"
Supported keys:
line-length: formatter print width for HTML and T-HTML templates onlyexclude: override the built-in default excludesextend-exclude: add more exclude patterns on top of the defaultsignore-file: path to a gitignore-style ignore file, relative to the project root
By default, t-linter also reads .t-linterignore from the project root if it exists.
Exit codes:
0: Run completed successfully1: Issues were found and--error-on-issueswas set2: Operational failure such as an unreadable file
Example input:
from typing import Annotated
from string.templatelib import Template
payload: Annotated[Template, "json"] = t"""[1,,2]"""
Example human output:
example.py:4:46: error[embedded-parse-error] Invalid json syntax in template string (language=json)
1 files scanned, 1 templates scanned, 1 diagnostics, 0 failed files
Example json output:
{
"files": [
{
"file": "example.py",
"template_count": 1,
"diagnostics": [
{
"rule": "embedded-parse-error",
"severity": "error",
"language": "json",
"message": "Invalid json syntax in template string",
"file": "example.py",
"start_line": 4,
"start_column": 46,
"end_line": 4,
"end_column": 47
}
]
}
],
"diagnostics": [
{
"rule": "embedded-parse-error",
"severity": "error",
"language": "json",
"message": "Invalid json syntax in template string",
"file": "example.py",
"start_line": 4,
"start_column": 46,
"end_line": 4,
"end_column": 47
}
],
"summary": {
"files_scanned": 1,
"templates_scanned": 1,
"diagnostics": 1,
"failed_files": 0
}
}
Example github output:
::error file=example.py,line=4,col=46,title=t-linter(embedded-parse-error)::Invalid json syntax in template string (language=json)
Get template string statistics (🚧 Coming soon):
# Analyze template string usage in your codebase
t-linter stats . # Current directory
t-linter stats src/ # Specific directory
# Expected output (when implemented):
# - Number of template strings by language
# - Template string locations
# - Language detection methods used
# - Type alias usage statistics
Roadmap
Planned Features
- ✅ Language Server Protocol (LSP) - Fully implemented
- ✅ Syntax Highlighting - Supports HTML, T-HTML, SQL, JavaScript, CSS, JSON, YAML, TOML
- ✅ Type Alias Support - Recognizes
type html = Annotated[Template, "html"] - ✅ Linting (
checkcommand) - Validate template strings for syntax errors - 🚧 Statistics (
statscommand) - Analyze template string usage across codebases - 📋 Cross-file Type Resolution - Track type aliases across module boundaries
- 📋 Auto-completion - Context-aware completions within template strings
Quick Start Example
Here's a complete example you can run through t-linter check right away. Each
helper function declares the embedded language once, and the interpolation
values are defined next to the template usage:
from typing import Annotated
from string.templatelib import Template
def render_html(template: Annotated[Template, "html"]) -> None:
pass
def run_sql(template: Annotated[Template, "sql"]) -> None:
pass
type css = Annotated[Template, "css"]
type yaml_config = Annotated[Template, "yaml"]
type toml_config = Annotated[Template, "toml"]
def load_styles(template: css) -> None:
pass
def load_yaml(template: yaml_config) -> None:
pass
def load_toml(template: toml_config) -> None:
pass
title = "t-linter"
heading = "Template strings with syntax highlighting"
content = "Interpolations stay as normal Python expressions."
render_html(t"""
<!DOCTYPE html>
<html>
<head>
<title>{title}</title>
</head>
<body>
<h1 style="color: #007acc">{heading}</h1>
<p>{content}</p>
</body>
</html>
""")
start_date = "2026-01-01"
run_sql(t"""
SELECT u.name, u.email, p.title
FROM users u
JOIN posts p ON u.id = p.author_id
WHERE u.created_at > {start_date}
ORDER BY u.name
""")
padding = 24
load_styles(t"""
.container {{
max-width: 1200px;
margin: 0 auto;
padding: {padding}px;
}}
""")
app_name = "demo-app"
load_yaml(t"""
app:
name: {app_name}
debug: true
""")
project_name = "demo-project"
version = "0.1.0"
load_toml(t"""
[project]
name = "{project_name}"
version = "{version}"
""")
Use {{ and }} when the embedded language needs literal braces, such as CSS
or JSON objects.
For html, <title>{value}</title> is allowed and treated as escaped text.
<script>, <style>, and <textarea> still reject interpolations for safety.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
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 t_linter-0.6.0.tar.gz.
File metadata
- Download URL: t_linter-0.6.0.tar.gz
- Upload date:
- Size: 91.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3f643e4b6a88ff60a51da8104b03a5cf23049b465a5400dec8f5f15546d3310a
|
|
| MD5 |
33035ff1686bb193f1ef4944a3ff1f05
|
|
| BLAKE2b-256 |
369b4cd8d2917c53a9e56df300aebbb0f6c772e1838e08f8f05ff397b21e7a48
|
File details
Details for the file t_linter-0.6.0-py3-none-win_amd64.whl.
File metadata
- Download URL: t_linter-0.6.0-py3-none-win_amd64.whl
- Upload date:
- Size: 3.7 MB
- Tags: Python 3, Windows x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9ad51eb328e209e16bd36c55bccd3b92218b06f2c252a6848b39b78bbc456be6
|
|
| MD5 |
df7ac3a33b03953c0e8a0a5869acecf3
|
|
| BLAKE2b-256 |
6b350dca52d2d2673b044e64e5411ad4e03e7a9e01bef4e575b583226881db3f
|
File details
Details for the file t_linter-0.6.0-py3-none-manylinux_2_28_x86_64.whl.
File metadata
- Download URL: t_linter-0.6.0-py3-none-manylinux_2_28_x86_64.whl
- Upload date:
- Size: 4.3 MB
- Tags: Python 3, manylinux: glibc 2.28+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
84c9a39b4c5683649625b6a41ccbb7aef223d29ae7b894a28d1579689eb6e540
|
|
| MD5 |
6d355bc8e3b1182fbaf67c4848ad306f
|
|
| BLAKE2b-256 |
90db84aad7bd6125df784c16d2e09ca00727696034ab3703449e6342fd32d5de
|
File details
Details for the file t_linter-0.6.0-py3-none-manylinux_2_28_aarch64.whl.
File metadata
- Download URL: t_linter-0.6.0-py3-none-manylinux_2_28_aarch64.whl
- Upload date:
- Size: 4.2 MB
- Tags: Python 3, manylinux: glibc 2.28+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d13ff5a40c72c0e81b100c10a924b43fd7114c323f2884573b62805dd74f21b7
|
|
| MD5 |
f98bcabfb4b830531f03720913ec5db3
|
|
| BLAKE2b-256 |
103a9181b4f8de818932fcc96128101caf35d68c0bc20ff6f5696c8eb056f03e
|
File details
Details for the file t_linter-0.6.0-py3-none-macosx_11_0_arm64.whl.
File metadata
- Download URL: t_linter-0.6.0-py3-none-macosx_11_0_arm64.whl
- Upload date:
- Size: 4.0 MB
- Tags: Python 3, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
830212f7c36020ed4366817b6ae8c5e4b97f151f51b49df599be38711dbadf91
|
|
| MD5 |
99bdbef77f326777fa18411c8ab24776
|
|
| BLAKE2b-256 |
89bc048ef25b868a0532a6c449f2cdcac8ab8fb753234363c3e4e29dd8dd3a70
|