f-string の限界と新しい選択肢

Python で文字列に値を埋め込む手段として、今やほとんどのコードで標準的に使われているのが f-string です。短く書けて読みやすい一方で、埋め込まれた値の扱いはすべて呼び出し側任せであるため、SQL やシェルコマンド・HTML のような「文脈ごとに適切なエスケープが必要な文字列」を組み立てる場面では、インジェクション脆弱性の原因になりやすい書き方でもあります。

2025 年 10 月にリリースされた Python 3.14 では、この問題にアプローチする新機能として t-string ( テンプレート文字列 ) が追加されました。PEP 750 で提案されたこの機能は、f-string と同じ書き心地を保ちながら、埋め込まれた値を生の値のまま保持して後から安全に処理できる仕組みを提供します。

本記事では、t-string の基本的な使い方と、HTML や SQL の組み立てに応用する実践的なパターンを解説します。

t-string とは

t-string は f-string と同じように t"..." の形で書けるテンプレート文字列です。見た目はそっくりですが、評価結果は str ではなく string.templatelib.Template オブジェクトになります。

from string.templatelib import Template

name = "Alice"
template = t"Hello, {name}!"
print(type(template))  # <class 'string.templatelib.Template'>

Template オブジェクトは、テンプレート内の「リテラル文字列」と「埋め込み値 ( Interpolation )」を分離した状態で保持しています。文字列全体を即座に連結するのではなく、組み立てに必要な素材がそのまま手元に残っている状態と考えるとイメージしやすいでしょう。

この素材に対して、利用側が目的に合わせた処理 ( エスケープ・クエリパラメータ化など ) を施すことで、文脈に適した安全な文字列を生成できます。

t-string の処理関数を書く

t-string そのものは「データ」であり、それを実際の文字列に変換する処理関数は利用側で用意します。Template はイテレート可能で、str と Interpolation が順番に取り出せる構造になっています。

from string.templatelib import Template

def render(template: Template) -> str:
    parts: list[str] = []
    for item in template:
        if isinstance(item, str):
            parts.append(item)
        else:
            parts.append(str(item.value))
    return "".join(parts)

name = "Alice"
print(render(t"Hello, {name}!"))  # Hello, Alice!

f-string と同じ結果を得るだけなら render のような単純な関数で十分ですが、t-string の本当の価値は「埋め込み値だけに特定の処理を適用する」ところにあります。

HTML エスケープを自動化する

t-string と相性が良い代表例が HTML の組み立てです。埋め込み値だけを自動的にエスケープする関数を用意しておけば、テンプレートを渡すだけで XSS 対策込みの安全な HTML を生成できます。

from html import escape
from string.templatelib import Template

def to_html(template: Template) -> str:
    parts: list[str] = []
    for item in template:
        if isinstance(item, str):
            parts.append(item)
        else:
            parts.append(escape(str(item.value)))
    return "".join(parts)

user_input = "<script>alert('xss')</script>"
print(to_html(t"<div>{user_input}</div>"))
# <div>&lt;script&gt;alert('xss')&lt;/script&gt;</div>

リテラル部分 ( <div> や </div> ) はそのまま残しつつ、埋め込み値だけが html.escape でサニタイズされます。f-string ではこの「リテラルはそのままで埋め込み値だけを加工する」処理を安全に書くことが難しかったため、テンプレートエンジンを使わずに HTML を組み立てる場面では大きな改善になります。

SQL クエリの安全な組み立て

同じ考え方は SQL の組み立てにも応用できます。埋め込み値をそのまま文字列結合するのではなく、プレースホルダーに置き換えて値はパラメータとして別に渡すことで、SQL インジェクションを根本的に回避できます。

from string.templatelib import Template

def to_sql(template: Template) -> tuple[str, list]:
    query_parts: list[str] = []
    params: list = []
    for item in template:
        if isinstance(item, str):
            query_parts.append(item)
        else:
            query_parts.append("?")
            params.append(item.value)
    return "".join(query_parts), params

user_id = 42
status = "active"
query, params = to_sql(t"SELECT * FROM users WHERE id = {user_id} AND status = {status}")
# query  -> "SELECT * FROM users WHERE id = ? AND status = ?"
# params -> [42, 'active']
cursor.execute(query, params)

呼び出し側は t-string でクエリを書くだけで、プレースホルダーへの置換とパラメータの切り出しを to_sql が自動的に行ってくれます。開発者が意識してバインディングを書き分ける必要がないため、レビュー時に SQL インジェクションを見逃しにくい構造を作れます。

使用時の注意点

  1. Python 3.14 以上でのみ動作する
    t-string は Python 3.14 で初めて導入された構文です。それ以前のバージョンでは SyntaxError になるため、実行環境やライブラリのバージョン要件に注意してください。
  2. 評価タイミングは f-string と同じ
    t-string 内の式は、f-string と同様にテンプレートを生成した時点で評価されます。Template が保持するのはあくまで「評価済みの値と、元の式文字列や変換指定」です。遅延評価を期待する機能ではない点に注意してください。
  3. 処理関数は利用者側が用意する
    t-string は「安全な文字列」を自動で返す機能ではありません。値をエスケープするか、プレースホルダーに置き換えるか、そのまま連結するかは処理関数の実装に委ねられています。ライブラリが t-string 用の API を提供していない場合は、用途に合わせた処理関数を自分で用意する必要があります。

まとめ

t-string は「f-string の読みやすさ」と「安全な文字列組み立て」の両立を目指して追加された Python 3.14 の新機能です。HTML・SQL・シェルコマンドなど、文脈に応じたエスケープが必要な場面で、従来よりも自然な書き心地でインジェクション対策を組み込めます。
まずは身近な HTML 生成や自作ユーティリティから取り入れ、Python 3.14 以降のプロジェクトでは t-string を前提にした安全な API 設計を検討してみてください。