New in 2026: Master Python for AI, Data Science

ProgrammingPython

Python 3.14 Beta — What’s Landing Before the Final Release

Python 3.14 Beta — What's Landing Before the Final Release

Python 3.14 is here — and it brings features that change how you write Python. Template strings that do not evaluate until you need them, annotations that defer their evaluation, and a locals() that finally does what you expect. Here is what shipped in Python 3.14 and what you need to know to update your codebases.

In this tutorial, you will learn to:

  • Understand what Python 3.14 shipped and how it differs from 3.13
  • Use t-strings (PEP 750) — the new template string literals in Python 3.14
  • Work with the annotationlib module (PEP 749) for lazy annotation evaluation
  • Navigate the improved locals() semantics from PEP 667
  • Upgrade to Python 3.14 and check your codebase for compatibility

What Is Python 3.14 and When Did It Ship?

Python 3.14 entered beta in 2025 with four planned beta releases. It crossed the finish line on October 7, 2025 — the official stable release, available now on python.org/downloads. According to PEP 745, the full release schedule looked like this:

PhaseDate
Beta 1May 6, 2025
Beta 2June 3, 2025
Beta 3June 17, 2025
Beta 4 (final beta)July 8, 2025
Release Candidate (RC)July 22, 2025
Final stable releaseOctober 7, 2025

If you are still on Python 3.13 or earlier, now is the time to plan your upgrade. Python 3.14 ships with several features that affect real-world Python code — t-strings, deferred annotations, and improved locals semantics are the three most likely to touch your day-to-day work.

Prerequisites

You should be comfortable with Python fundamentals — functions, classes, strings, and type annotations. If you need a refresher on strings, see the Python strings guide. For type annotations, the type hints guide covers everything you need.

t-Strings: Template Strings (PEP 750)

t-strings — introduced by PEP 750 — are a new string prefix in Python 3.14. They look like f-strings but produce a Template object instead of a plain string. That object retains the interpolation structure so frameworks like ORMs and query builders can inspect, modify, or sanitize the template before it is evaluated.

The key difference from f-strings is that t-strings are not immediately evaluated. They defer processing to whatever receives them:

# f-string — evaluated immediately
name = "Alice"
greeting = f"Hello, {name}!"
print(greeting)  # Hello, Alice!

# t-string — produces a Template object, not a string
from template import Template  # new module in Python 3.14
name = "Alice"
greeting = t"Hello, {name}!"
print(type(greeting))  # <class 'Template'>
print(greeting)        # Hello, {name}!  (placeholder preserved)

When a t-string is passed to a function that accepts a Template, the function can iterate over the placeholders, substitute values safely, or reject unsafe input. This makes t-strings a powerful tool for building safe query interfaces:

# Simulated ORM query builder receiving a t-string
def query(sql: Template) -> list[dict]:
    # The ORM can inspect sql.placeholders before executing
    print(f"Placeholders: {sql.placeholders}")
    # -> ['user_id']
    # Safe to substitute, log, or reject before execution
    return []

When to Use t-Strings vs f-Strings

ScenarioUse
Building SQL or DSL queriest-strings — frameworks can inspect before execution
Internationalization (i18n) templatest-strings — placeholders stay intact for translation tools
Simple string interpolation in application codef-strings — immediate evaluation is fine
Logging with structured placeholderst-strings — log systems can enrich before rendering

t-strings are not a replacement for f-strings — they are a specialized tool for cases where the template structure itself carries meaning that a consumer needs to act on. Read the official Python 3.14 whatsnew docs for the full specification.

How PEP 667 Improves locals() Semantics

PEP 667 brings formally defined and consistent semantics to locals() and frame.f_locals. In earlier Python versions, locals() had well-known quirks: the returned mapping behaved inconsistently depending on the execution scope, and mutating it did not reliably affect local variables.

Python 3.13 introduced a low-overhead dynamic frame locals access mechanism as an experimental feature. Python 3.14 stabilizes and builds on it — locals() now has defined, consistent behavior across function, class, and module scopes, making debuggers, profilers, and meta-programming tools far more reliable:

import sys

def inspect_frame():
    x = 42
    y = "hello"
    # f_locals now reliably reflects current frame locals
    frame = sys._getframe()
    print(frame.f_locals)  # {'x': 42, 'y': 'hello', ...}

    # Mutation of f_locals now correctly updates local variables
    frame.f_locals['z'] = 100
    print(z)  # 100 — z is now a real local variable

inspect_frame()

If you write debuggers, profilers, or tools that read frame locals, PEP 667 is a significant improvement. For regular application code, you benefit indirectly — any third-party tooling you use now has a stable interface to work with.

The annotationlib Module: Lazy Annotation Evaluation (PEP 749)

PEP 749 implements deferred evaluation of annotations — a capability that PEP 649 began standardizing. Python 3.14 ships this with a new standard library module: annotationlib.

Previously, annotations were evaluated eagerly at definition time. This caused import-time side effects and circular dependency problems, especially in large codebases. With annotationlib, annotations are stored as strings and evaluated lazily — only when your tooling explicitly requests them:

# Without annotationlib — eager evaluation (pre-3.14 behavior)
class User:
    name: str           # evaluated immediately at class definition
    age: int            # NameError possible if dependencies not yet defined

# With annotationlib — deferred evaluation
import annotationlib

class User:
    name: str
    age: int

# Inspect annotations without triggering evaluation
for name, value in annotationlib.get_annotations(User).items():
    print(f"{name}: {value}")  # name: <class 'str'>, age: <class 'int'>

The annotationlib module provides three evaluation formats:

  • Value format — fully evaluated, like pre-3.14 eager behavior
  • String format — raw annotation strings, no evaluation
  • Forward reference format — preserves forward references without requiring from __future__ import annotations
import annotationlib

class Config:
    db_host: str
    db_port: int
    max_connections: "int | None"  # forward reference, no quotes needed

# Get annotations in string form
annots = annotationlib.get_annotations(Config, format=annotationlib.Format.STRING)
print(annots)
# {'db_host': 'str', 'db_port': 'int', 'max_connections': 'int | None'}

# Get fully evaluated annotations
annots_value = annotationlib.get_annotations(Config, format=annotationlib.Format.VALUE)
print(annots_value)
# {'db_host': <class 'str'>, 'db_port': <class 'int'>, ...}

Why it matters: The from __future__ import annotations import made annotation evaluation lazy in Python 3.7+, but it was a file-wide flag with its own quirks. annotationlib gives you fine-grained control per-class or per-function, without requiring a future import at the top of every module.

How to Upgrade to Python 3.14 and Test Your Codebase

Option 1: Download Directly from Python.org

The official downloads are at python.org/downloads. Choose the installer for your platform — Windows, macOS, or Linux. You want the 3.14.x latest stable release, not an older beta.

Option 2: Use pyenv

# Install Python 3.14 stable via pyenv
pyenv install 3.14.0

# Create a dedicated virtual environment
pyenv virtualenv 3.14.0 myproject-314

# Activate and verify
pyenv local myproject-314
python --version  # Python 3.14.0

Option 3: Use uv (Recommended)

If you have uv installed, upgrading takes seconds:

# Install Python 3.14 and set as default
uv python install 3.14.0
uv python default 3.14.0

# Or pin a project to 3.14
uv init python314-upgrade
cd python314-upgrade
uv python pin 3.14.0

# Verify
python --version  # Python 3.14.0

Testing Your Existing Codebase

Once you have Python 3.14 in a virtual environment, run your test suite to catch compatibility issues:

# Activate your environment first, then:
uv run pytest        # if using uv
python -m pytest     # standard approach

# Check for deprecation warnings (Python 3.14 may emit new warnings)
python -W all -m pytest

# Check your project works with new syntax
python -m py_compile src/**/*.py

Test in a virtual environment first. Never upgrade Python in a production environment without running your test suite against the new version in a staging setup. Use pyenv or uv to manage multiple Python versions side by side on the same machine.

Common Mistakes / Gotchas

  • t-strings are not f-strings: Passing a t-string to print() prints the raw template, not an interpolated value. Use f-strings for normal interpolation. Only use t-strings when a consumer function needs to inspect the template structure.
  • annotationlib requires explicit import: The lazy evaluation behavior is opt-in via import annotationlib — it does not change how __annotations__ behaves on existing classes unless you use the module explicitly.
  • locals() mutation is frame-specific: Mutating frame.f_locals only works within the active frame. You cannot use PEP 667 to mutate locals in a parent scope from a nested function.
  • Free-threaded Python is separate: Python 3.14 ships free-threaded mode as an official experimental feature — but it requires a separate build (python3.14t). Do not confuse the main release with the free-threaded variant. Check the official whatsnew page for the specific build you need.

Summary & Next Steps

Python 3.14 ships three headline features worth knowing: t-strings (PEP 750) for template-aware string processing, the annotationlib module (PEP 749) for lazy annotation evaluation, and defined locals() semantics (PEP 667) that make introspection tools more reliable. Python 3.14 shipped on October 7, 2025 — if you have not upgraded yet, your window is open now.

Related posts
Python

Pydantic Agent Basics: A Complete 2026 Tutorial

ProgrammingPython

Production-Ready MCP Servers — Security, Testing & Deployment

ProgrammingPython

Build Your First MCP Server with Python SDK — Fundamentals

ProgrammingPython

Connect FastAPI to MCP — Two Integration Patterns

Leave a Reply