Torna al blog

Python 3.15: Sampling Profiler, UTF-8 Default, AsyncPaginator e Miglioramenti

Esplora Python 3.15: statistical sampling profiler ad alta frequenza, UTF-8 encoding di default, AsyncPaginator, miglioramenti AttributeError e math.isnormal().

Edoardo Midali

Edoardo Midali

Developer · Content Creator

9 min di lettura
Python 3.15: Sampling Profiler, UTF-8 Default, AsyncPaginator e Miglioramenti

Python 3.15 introduce un sampling profiler statistico ad alta frequenza per performance analysis in produzione, UTF-8 come encoding di default, AsyncPaginator per paginazione asincrona, miglioramenti ai messaggi di errore AttributeError e nuove funzioni math. Questa release migliora debugging, internazionalizzazione e developer experience.

🎯 Novità Principali

Statistical Sampling Profiler (PEP 799)

Profiling production con zero overhead:

# ✅ Python 3.15 - Sampling profiler

# Profile processo in esecuzione
python -m profiling.sampling 1234

# Con opzioni custom
python -m profiling.sampling \
    -i 50 \              # 50μs interval
    -d 30 \              # 30 secondi duration
    -o profile.stats \   # Output file
    1234                 # Process ID

# Generate flamegraph data
python -m profiling.sampling --collapsed 1234

# Profile all threads
python -m profiling.sampling -a --sort-tottime 1234

Real-time statistics:

# ✅ Output esempio

# Real-time sampling stats:
# Mean: 100261.5Hz (9.97µs)
# Min: 86333.4Hz (11.58µs)
# Max: 118807.2Hz (8.42µs)
# Samples: 400001

# Captured 498841 samples in 5.00 seconds
# Sample rate: 99768.04 samples/sec
# Error rate: 0.72%

# Profile Stats:
# nsamples sample% tottime cumul% cumtime  filename:lineno(function)
# 158562   33.3%   1.586s  33.3%  1.586s   test_compile.py:725(check_limit)
# 129553   27.2%   1.296s  27.2%  1.296s   ast.py:46(parse)
# 22361    4.7%    0.224s  4.7%   0.224s   test_compile.py:1368(test_big_dict)

Key features:

# ✅ Caratteristiche profiler

# Zero overhead: attach senza impact performance
# No code modification: profila app esistenti
# Real-time stats: monitora qualità sampling
# Multiple formats: stats dettagliati + flamegraph
# Thread-aware: profila main thread o tutti

# Sampling rates: fino a 1,000,000 Hz!
# Il profiler più veloce per Python

Programmatic usage:

# ✅ API Python

from profiling.sampling import SamplingProfiler

# Profila funzione
profiler = SamplingProfiler(interval_us=100)
profiler.start()

# Codice da profilare
result = expensive_function()

profiler.stop()
stats = profiler.get_stats()

# Analizza hot spots
for func, samples in stats.items():
    print(f"{func}: {samples.direct_samples} samples")

# Identifica bottlenecks automaticamente
hot_spots = stats.get_hot_spots()
for spot in hot_spots:
    print(f"Hot spot: {spot.function} - {spot.percentage}%")

Production debugging:

# ✅ Debug performance in produzione

# 1. Attach al processo
# python -m profiling.sampling --duration=60 <PID>

# 2. Profiler identifica bottleneck
# Hot spot: data_processing.py:42(transform_data) - 45%

# 3. Analizza pattern
# High call magnification: 12267x
# Molte chiamate indirette da poche chiamate dirette

# 4. Fix problema senza restart!

# Ideale per:
# - Production debugging
# - Performance regression
# - Unexpected slowdowns
# - CPU profiling

UTF-8 Default Encoding

UTF-8 indipendente da sistema:

# ❌ Prima: encoding dipendeva da locale

# Windows (locale CP1252):
with open('file.txt', 'w') as f:
    f.write('Hello 世界')  # Errore su caratteri non-ASCII

# Linux (locale UTF-8):
with open('file.txt', 'w') as f:
    f.write('Hello 世界')  # OK

# Comportamento inconsistente!
# ✅ Python 3.15 - UTF-8 di default

# Su qualsiasi sistema operativo:
with open('file.txt', 'w') as f:
    f.write('Hello 世界 🌍')  # OK ovunque!

# UTF-8 standard:
# - ~99% del web
# - Standard de facto
# - Supporto Unicode completo

# Encoding esplicito ancora possibile:
with open('file.txt', 'w', encoding='latin-1') as f:
    f.write('Data')

# encoding='locale' per vecchio comportamento
with open('file.txt', 'w', encoding='locale') as f:
    f.write('Data')

Opt-out se necessario:

# ✅ Disable UTF-8 mode

# Environment variable
PYTHONUTF8=0 python app.py

# Command-line
python -X utf8=0 app.py

# Encoding warning per compatibilità
python -X warn_default_encoding app.py
# Identifica codice che potrebbe essere affetto

Migration guide:

# ✅ Best practices per compatibilità

# SEMPRE specifica encoding per compatibilità versioni
with open('file.txt', 'w', encoding='utf-8') as f:
    f.write('Data')

# Usa encoding warning durante test
# PYTHONWARNDEFAULTENCODING=1 pytest

# Identifica codice da aggiornare:
# - open() senza encoding
# - file operations implicit encoding
# - subprocess text mode

# Fix automatico:
# 1. Aggiungi encoding='utf-8' esplicito
# 2. Oppure encoding='locale' se necessario locale

Improved AttributeError Messages

Suggerimenti per attributi nested:

# ✅ Python 3.15 - Nested attribute suggestions

from dataclasses import dataclass
from math import pi

@dataclass
class Circle:
    radius: float

    @property
    def area(self) -> float:
        return pi * self.radius ** 2

class Container:
    def __init__(self, inner: Circle) -> None:
        self.inner = inner

circle = Circle(radius=4.0)
container = Container(circle)

# Accedi attributo errato
print(container.area)

# Output Python 3.14:
# AttributeError: 'Container' object has no attribute 'area'

# Output Python 3.15:
# AttributeError: 'Container' object has no attribute 'area'.
# Did you mean: 'inner.area'?

# Suggerisce path completo!

delattr() suggestions:

# ✅ Suggerimenti anche per delattr()

class A:
    pass

a = A()
a.abcde = 1

# Typo durante delete
del a.abcdf

# Python 3.14:
# AttributeError: 'A' object has no attribute 'abcdf'

# Python 3.15:
# AttributeError: 'A' object has no attribute 'abcdf'.
# Did you mean: 'abcde'?

AsyncPaginator

Paginazione asincrona nativa:

# ✅ Python 3.15 - AsyncPaginator

from django.core.paginator import AsyncPaginator

async def list_articles(request):
    articles = Article.objects.all()

    paginator = AsyncPaginator(articles, per_page=25)
    page_number = request.GET.get('page', 1)

    page = await paginator.aget_page(page_number)

    return {
        'articles': [
            {'title': a.title, 'id': a.id}
            async for a in page
        ],
        'has_next': page.has_next(),
        'has_previous': page.has_previous(),
    }

AsyncPage:

# ✅ Async iteration

from django.core.paginator import AsyncPaginator

async def process_large_dataset():
    data = await fetch_large_dataset()

    paginator = AsyncPaginator(data, per_page=100)

    results = []
    for page_num in paginator.page_range:
        page = await paginator.aget_page(page_num)

        async for item in page:
            processed = await process_item(item)
            results.append(processed)

    return results

🔧 Module Improvements

math Module

# ✅ Nuove funzioni math

import math

# isnormal() - check normal float
print(math.isnormal(1.0))     # True
print(math.isnormal(0.0))     # False
print(math.isnormal(float('inf')))  # False

# issubnormal() - check subnormal float
print(math.issubnormal(1e-308))  # True
print(math.issubnormal(1.0))     # False

# fmax() / fmin() - max/min con NaN handling
print(math.fmax(1.0, float('nan')))  # 1.0
print(math.fmin(1.0, float('nan')))  # 1.0
print(max(1.0, float('nan')))        # nan (different!)

# signbit() - check sign bit
print(math.signbit(-0.0))  # True
print(math.signbit(0.0))   # False

calendar Module

# ✅ Dark mode support + HTML5

import calendar

# HTML calendar con dark mode
cal = calendar.HTMLCalendar()
html = cal.formatmonth(2024, 12)

# Output:
# <!DOCTYPE html>
# <html>
# <head>
#   <meta name="color-scheme" content="light dark">
#   <style>
#     @media (prefers-color-scheme: dark) {
#       /* Dark mode styles */
#     }
#   </style>
# </head>
# ...

# Accessibility improvements:
# - Semantic HTML5
# - ARIA labels
# - Keyboard navigation

difflib Module

# ✅ Color output

import difflib

text1 = "Hello World\nPython is great"
text2 = "Hello Universe\nPython is awesome"

# Unified diff con colori
diff = difflib.unified_diff(
    text1.splitlines(keepends=True),
    text2.splitlines(keepends=True),
    color=True  # Nuovo parametro!
)

print(''.join(diff))
# Output colorato come git diff:
# - linee rimosse in rosso
# + linee aggiunte in verde

# Controllo tramite environment variables:
# NO_COLOR, FORCE_COLOR, TERM

sqlite3 Module

# ✅ CLI improvements

# SQL keyword completion con <tab>
$ python -m sqlite3 database.db
sqlite> SEL<tab>  → SELECT

# Colored output (di default)
sqlite> SELECT * FROM users;
# Risultati colorati per leggibilità

# Help text colorato
sqlite> .help

os Module

# ✅ os.statx() su Linux

import os

# statx() - extended file info (Linux 4.11+)
stat = os.statx(
    'file.txt',
    dir_fd=None,
    flags=0,
    mask=os.STATX_ALL
)

print(stat.st_size)      # File size
print(stat.st_blocks)    # Allocated blocks
print(stat.st_blksize)   # Block size
print(stat.st_btime)     # Birth time (creation)
print(stat.st_mtime)     # Modification time

# Più dettagliato di os.stat()

🎨 Language Enhancements

Warning Filters con Regex

# ✅ Python 3.15 - Regex in warning filters

# Command line
python -W '/deprecation.*/::DeprecationWarning:mymodule' app.py

# PYTHONWARNINGS environment variable
export PYTHONWARNINGS='/future.*/:FutureWarning:/'

# In code
import warnings
warnings.filterwarnings(
    'ignore',
    message='/old.*api/',  # Regex!
    category=DeprecationWarning,
    module='/my.*module/',  # Regex!
)

# Match pattern: /regex/
# Prima: solo literal strings
# Ora: potenti regex per filtering

Real Number Support

# ✅ Timestamp con Decimal/Fraction

from decimal import Decimal
from fractions import Fraction
import time

# Timestamp precisi
timestamp = Decimal('1234567890.123456789')
time.sleep(timestamp)  # Funziona!

# Fraction timeouts
timeout = Fraction(1, 2)  # 0.5 secondi
socket.settimeout(timeout)

# Prima: solo int/float
# Ora: qualsiasi real number
# (non migliora precisione, solo convenience)

Enhanced repr

# ✅ ImportError/ModuleNotFoundError repr

try:
    import nonexistent_module
except ImportError as e:
    print(repr(e))

# Python 3.14:
# ImportError()

# Python 3.15:
# ImportError(name='nonexistent_module', path=None)

# Mostra name e path se forniti!
# Migliore debugging

📊 Deprecations & Removals

Removed

# ❌ Removed in Python 3.15

# ctypes.SetPointerType()
from ctypes import SetPointerType  # ImportError!

# glob.glob0() / glob.glob1()
from glob import glob0, glob1  # ImportError!
# Use glob.glob() instead

# http.server.CGIHTTPRequestHandler
from http.server import CGIHTTPRequestHandler  # ImportError!

# pathlib.PurePath.is_reserved()
from pathlib import Path
Path('CON').is_reserved()  # AttributeError!
# Use os.path.isreserved() on Windows

# platform.java_ver()
from platform import java_ver  # ImportError!

# sre_compile, sre_constants, sre_parse
import sre_compile  # ImportError!
# Use re module instead

# typing.ByteString removed from __all__
from typing import ByteString  # DeprecationWarning

Deprecated

# ⚠️ Deprecated in Python 3.15

# -b / -bb options (bytes warning)
python -b app.py  # DeprecationWarning
# Diventerà no-op in 3.17

# hash function string= parameter
import hashlib
hashlib.md5(string=b'data')  # DeprecationWarning
# Use positional: hashlib.md5(b'data')

# __version__ attributes
import ctypes
print(ctypes.__version__)  # DeprecationWarning
# Use sys.version_info

# Bitwise inversion on bools
result = ~True   # DeprecationWarning (-2)
result = ~False  # DeprecationWarning (-1)
# Use: not True, not False

🎓 Best Practices

1. Use Sampling Profiler in Production

# ✅ Profile senza impatto

# Attach al processo
python -m profiling.sampling --duration=60 <PID>

# Identifica bottleneck
# Fix senza restart
# Zero overhead

2. Always Specify Encoding

# ✅ Explicit encoding per compatibilità

# Buono
with open('file.txt', 'w', encoding='utf-8') as f:
    f.write('Data')

# Evita
with open('file.txt', 'w') as f:  # Implicit UTF-8
    f.write('Data')

3. Use Async Paginator

# ✅ Per large datasets async

async def process_data():
    paginator = AsyncPaginator(data, per_page=100)

    async for item in paginator.aget_page(1):
        await process_item(item)

4. Leverage Improved Error Messages

# ✅ Let Python guide you

# Python suggests:
# 'container.inner.area'
# 'abcde' instead of 'abcdf'

# Faster debugging!

💡 Conclusioni

Python 3.15 migliora debugging e internazionalizzazione:

Sampling profiler fino a 1MHz ✅ UTF-8 di default cross-platform ✅ AsyncPaginator per large datasets ✅ AttributeError con nested suggestions ✅ math.isnormal() e nuove funzioni ✅ Warning filters con regex ✅ Color support in CLI tools

Upgrade oggi:

# Installa Python 3.15
# pyenv
pyenv install 3.15.0
pyenv global 3.15.0

# Or download from python.org
# https://www.python.org/downloads/

# Verifica versione
python --version  # Python 3.15.0

Quando usare Python 3.15:

  • ✅ Nuovi progetti
  • ✅ Production profiling necessario
  • ✅ Multi-platform UTF-8
  • ✅ Async web applications
  • ✅ Large dataset processing