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
annotationlibmodule (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
Page Contents
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:
| Phase | Date |
|---|---|
| Beta 1 | May 6, 2025 |
| Beta 2 | June 3, 2025 |
| Beta 3 | June 17, 2025 |
| Beta 4 (final beta) | July 8, 2025 |
| Release Candidate (RC) | July 22, 2025 |
| Final stable release | October 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
| Scenario | Use |
|---|---|
| Building SQL or DSL queries | t-strings — frameworks can inspect before execution |
| Internationalization (i18n) templates | t-strings — placeholders stay intact for translation tools |
| Simple string interpolation in application code | f-strings — immediate evaluation is fine |
| Logging with structured placeholders | t-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 annotationsimport made annotation evaluation lazy in Python 3.7+, but it was a file-wide flag with its own quirks.annotationlibgives 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
pyenvoruvto 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_localsonly 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.
- What’s New in Python 3.14 — official documentation
- PEP 750 — Template Strings — full specification
- PEP 749 — Implementing PEP 649 — annotationlib specification
- PEP 667 — Consistent views of namespaces — locals() semantics
- Python 3.14 downloads — official release
- Python Strings: The Complete Guide — f-strings, t-strings, and all string methods
- Python Type Hints & Annotations: From Beginner to Pro — deep dive on annotation patterns

