Skip to main content

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).

License: MIT VSCode Marketplace PyPI

📣 💼 Maintainer update: Open to opportunities. 🔗 koxudaxi.dev

📖 Documentation

👉 t-linter.koxudaxi.dev


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

T-Linter VSCode Extension in action

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 highlighting
  • check: strict parsing through the tstring-html, tstring-thtml, tstring-json, tstring-yaml, and tstring-toml backends
  • formatting: 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:

  1. Open VSCode
  2. Go to Extensions (Ctrl+Shift+X / Cmd+Shift+X)
  3. Search for "t-linter"
  4. 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:

  1. Open VSCode Settings (Ctrl+, / Cmd+,)
  2. Search for "python.languageServer"
  3. 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:

  1. Find your t-linter path by running in terminal:

    which t-linter     # macOS/Linux
    where t-linter     # Windows
    
  2. Open VSCode Settings (Ctrl+, / Cmd+,)

  3. Search for "t-linter.serverPath"

  4. 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-linter or /usr/local/bin/t-linter
    • Linux: /home/yourname/.local/bin/t-linter or /usr/local/bin/t-linter

→ 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.

→ View on PyPI

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:

  1. Ensure t-linter is installed: Run t-linter --version in terminal
  2. Check that Python language server is disabled: python.languageServer should be set to "None"
  3. Check the server path in VSCode settings: t-linter.serverPath
  4. 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 only
  • exclude: override the built-in default excludes
  • extend-exclude: add more exclude patterns on top of the defaults
  • ignore-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 successfully
  • 1: Issues were found and --error-on-issues was set
  • 2: 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 (check command) - Validate template strings for syntax errors
  • 🚧 Statistics (stats command) - 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


Download files

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

Source Distribution

t_linter-0.6.0.tar.gz (91.5 kB view details)

Uploaded Source

Built Distributions

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

t_linter-0.6.0-py3-none-win_amd64.whl (3.7 MB view details)

Uploaded Python 3Windows x86-64

t_linter-0.6.0-py3-none-manylinux_2_28_x86_64.whl (4.3 MB view details)

Uploaded Python 3manylinux: glibc 2.28+ x86-64

t_linter-0.6.0-py3-none-manylinux_2_28_aarch64.whl (4.2 MB view details)

Uploaded Python 3manylinux: glibc 2.28+ ARM64

t_linter-0.6.0-py3-none-macosx_11_0_arm64.whl (4.0 MB view details)

Uploaded Python 3macOS 11.0+ ARM64

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

Hashes for t_linter-0.6.0.tar.gz
Algorithm Hash digest
SHA256 3f643e4b6a88ff60a51da8104b03a5cf23049b465a5400dec8f5f15546d3310a
MD5 33035ff1686bb193f1ef4944a3ff1f05
BLAKE2b-256 369b4cd8d2917c53a9e56df300aebbb0f6c772e1838e08f8f05ff397b21e7a48

See more details on using hashes here.

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

Hashes for t_linter-0.6.0-py3-none-win_amd64.whl
Algorithm Hash digest
SHA256 9ad51eb328e209e16bd36c55bccd3b92218b06f2c252a6848b39b78bbc456be6
MD5 df7ac3a33b03953c0e8a0a5869acecf3
BLAKE2b-256 6b350dca52d2d2673b044e64e5411ad4e03e7a9e01bef4e575b583226881db3f

See more details on using hashes here.

File details

Details for the file t_linter-0.6.0-py3-none-manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for t_linter-0.6.0-py3-none-manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 84c9a39b4c5683649625b6a41ccbb7aef223d29ae7b894a28d1579689eb6e540
MD5 6d355bc8e3b1182fbaf67c4848ad306f
BLAKE2b-256 90db84aad7bd6125df784c16d2e09ca00727696034ab3703449e6342fd32d5de

See more details on using hashes here.

File details

Details for the file t_linter-0.6.0-py3-none-manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for t_linter-0.6.0-py3-none-manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 d13ff5a40c72c0e81b100c10a924b43fd7114c323f2884573b62805dd74f21b7
MD5 f98bcabfb4b830531f03720913ec5db3
BLAKE2b-256 103a9181b4f8de818932fcc96128101caf35d68c0bc20ff6f5696c8eb056f03e

See more details on using hashes here.

File details

Details for the file t_linter-0.6.0-py3-none-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for t_linter-0.6.0-py3-none-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 830212f7c36020ed4366817b6ae8c5e4b97f151f51b49df599be38711dbadf91
MD5 99bdbef77f326777fa18411c8ab24776
BLAKE2b-256 89bc048ef25b868a0532a6c449f2cdcac8ab8fb753234363c3e4e29dd8dd3a70

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page