π Series Navigation:
- Chapter 1: Deferred Annotations & Multiple Interpreters β [Read Chapter 1]
- Chapter 2 (You are here): Template Strings & Exception Handling
- Chapter 3: Control Flow in Finally Blocks & Key Takeaways β [Read Chapter 3]
Welcome back! In Chapter 1, we explored deferred annotations and multiple interpreters. Now let's dive into two more game-changing features that will make your code more secure and readable.
3. PEP 750: Template Strings - Type-Safe String Formatting
What's New?
Template strings introduce a powerful and safe way to work with strings, especially for SQL queries, HTML templates, and shell commands, preventing injection attacks by design.
Basic Syntax
# Template strings with t"" prefix
from typing import Template
name = "Alice"
age = 30
# Simple template string
message = t"Hello, {name}! You are {age} years old."
print(message)
# Template strings preserve structure
user_input = "Robert'; DROP TABLE users;--"
query = t"SELECT * FROM users WHERE name = {user_input}"
# Python 3.14 automatically escapes dangerous inputs!
Real-World Example: SQL Query Builder
from typing import Template
from datetime import date
class DatabaseQuery:
"""Type-safe SQL query builder using template strings"""
@staticmethod
def get_user_by_email(email: str) -> Template:
# Automatic escaping prevents SQL injection
return t"SELECT * FROM users WHERE email = {email}"
@staticmethod
def get_orders_by_date(user_id: int, start_date: date) -> Template:
return t"""
SELECT o.id, o.total, o.created_at
FROM orders o
WHERE o.user_id = {user_id}
AND o.created_at >= {start_date}
ORDER BY o.created_at DESC
"""
@staticmethod
def safe_search(search_term: str, category: str) -> Template:
# Even malicious inputs are handled safely
return t"""
SELECT p.name, p.price, p.description
FROM products p
WHERE p.category = {category}
AND p.name LIKE {search_term}
"""
# Usage
db = DatabaseQuery()
# Safe even with malicious input
malicious_input = "admin' OR '1'='1"
query = db.get_user_by_email(malicious_input)
# Result: SELECT * FROM users WHERE email = 'admin'' OR ''1''=''1'
# Properly escaped!
search_query = db.safe_search("%laptop%", "electronics")
print(search_query)
Real-World Example: HTML Template Engine
from typing import Template
from html import escape
class HTMLBuilder:
"""Type-safe HTML generation"""
@staticmethod
def render_user_card(username: str, bio: str, posts_count: int) -> Template:
# XSS protection built-in
return t"""
<div class="user-card">
<h2>{username}</h2>
<p>{bio}</p>
<span class="badge">{posts_count} posts</span>
</div>
"""
@staticmethod
def render_comment(author: str, content: str, timestamp: str) -> Template:
return t"""
<div class="comment">
<strong>{author}</strong>
<p>{content}</p>
<time>{timestamp}</time>
</div>
"""
# Usage
html_builder = HTMLBuilder()
# Even if user inputs malicious content, it's escaped
xss_attempt = "<script>alert('XSS')</script>"
card = html_builder.render_user_card(
username="hacker",
bio=xss_attempt,
posts_count=42
)
# Output safely escapes the script tag!
print(card)
Benefits for Clean Code
β
Automatic escaping prevents injection vulnerabilities
β
Type-safe interpolation catches errors at development time
β
Cleaner syntax than format strings or concatenation
β
Built-in security for web and database applications
4. PEP 758: Except and Except* Expressions Without Brackets - Cleaner Exception Handling
What's New?
Python 3.14 allows exception handling without brackets for single exception types, making code more readable and reducing visual noise.
Old Way vs New Way
# OLD WAY (Python 3.13 and earlier)
try:
result = risky_operation()
except (ValueError): # Unnecessary brackets for single exception
handle_value_error()
# NEW WAY (Python 3.14)
try:
result = risky_operation()
except ValueError: # Clean and simple!
handle_value_error()
# Still works for multiple exceptions
try:
result = another_operation()
except ValueError, TypeError, KeyError: # More readable!
handle_multiple_errors()
Exception Groups (except*)
# Python 3.14 simplifies exception group handling
try:
async with asyncio.TaskGroup() as group:
group.create_task(fetch_data_1())
group.create_task(fetch_data_2())
group.create_task(fetch_data_3())
except* ConnectionError: # Clean syntax for exception groups
print("Some connections failed")
except* TimeoutError:
print("Some operations timed out")
except* ValueError:
print("Some data was invalid")
Real-World Example: API Error Handling
import httpx
from typing import Optional
import asyncio
class APIClient:
"""Clean exception handling for API requests"""
async def fetch_user(self, user_id: int) -> Optional[dict]:
try:
async with httpx.AsyncClient() as client:
response = await client.get(f"https://api.example.com/users/{user_id}")
response.raise_for_status()
return response.json()
# Clean single exception handling
except httpx.TimeoutError:
print(f"Request timed out for user {user_id}")
return None
except httpx.NetworkError:
print(f"Network error for user {user_id}")
return None
except httpx.HTTPStatusError:
print(f"HTTP error for user {user_id}")
return None
async def fetch_multiple_users(self, user_ids: list[int]) -> list[dict]:
"""Fetch multiple users with exception group handling"""
async with asyncio.TaskGroup() as group:
tasks = [
group.create_task(self.fetch_user(uid))
for uid in user_ids
]
# Handle exception groups cleanly
try:
results = [task.result() for task in tasks]
return [r for r in results if r is not None]
except* httpx.TimeoutError:
print("Some requests timed out")
except* httpx.NetworkError:
print("Network issues encountered")
return []
# Usage
async def main():
client = APIClient()
users = await client.fetch_multiple_users([1, 2, 3, 4, 5])
print(f"Successfully fetched {len(users)} users")
asyncio.run(main())
Real-World Example: File Processing
from pathlib import Path
from typing import List
import json
class FileProcessor:
"""Clean exception handling for file operations"""
def read_json_file(self, filepath: Path) -> dict:
try:
with open(filepath, 'r') as f:
return json.load(f)
except FileNotFoundError:
print(f"File not found: {filepath}")
return {}
except json.JSONDecodeError:
print(f"Invalid JSON in: {filepath}")
return {}
except PermissionError:
print(f"Permission denied: {filepath}")
return {}
def process_multiple_files(self, filepaths: List[Path]) -> List[dict]:
"""Process multiple files with clean error handling"""
results = []
for filepath in filepaths:
try:
data = self.read_json_file(filepath)
if data:
results.append(data)
# Clean handling without brackets
except OSError:
print(f"OS error processing {filepath}")
continue
except Exception:
print(f"Unexpected error with {filepath}")
continue
return results
# Usage
processor = FileProcessor()
files = [Path(f"data_{i}.json") for i in range(5)]
data = processor.process_multiple_files(files)
Benefits for Clean Code
β
Reduced visual noise - no unnecessary brackets
β
More readable exception handling
β
Cleaner syntax for exception groups
β
Consistent style across codebases
π Continue Reading
You're making great progress! Continue to Chapter 3 for the final feature and key takeaways.
π [Continue to Chapter 3 β]
π Complete Series Navigation
Part 1 - Modern Features Guide:
- Chapter 1: Deferred Annotations & Multiple Interpreters β [Back to Chapter 1]
- Chapter 2 (Current): Template Strings & Exception Handling
- Chapter 3: Control Flow & Summary
Keywords: Python 3.14, template strings, exception handling, SQL injection prevention, XSS protection, PEP 750, PEP 758, Python security
Top comments (0)