Introduzione
Quando si lavora con i file, non è sufficiente saperli creare, leggere o modificare. In molti programmi è necessario conoscere anche le loro informazioni tecniche: dimensione, data di modifica, proprietario, permessi di lettura e scrittura e tipo di elemento. Questi dati sono chiamati metadati.
La gestione dei metadati è utile, per esempio, per realizzare strumenti di manutenzione, controllare se un file è stato aggiornato, individuare file troppo grandi oppure verificare che un documento non sia modificabile da utenti non autorizzati.
Python offre diverse funzioni per questo scopo. Il modulo os permette di accedere ai permessi e agli attributi del sistema operativo, mentre il modulo stat aiuta a interpretarli in modo leggibile. Le operazioni sui permessi dipendono dal sistema: i permessi Unix, come Linux e macOS, sono più dettagliati rispetto a quelli normalmente disponibili su Windows.
Codice completo
from pathlib import Path
from datetime import datetime
import os
import stat
def formatta_dimensione(byte):
"""Converte i byte in un formato più leggibile."""
unita = ["B", "KB", "MB", "GB"]
dimensione = float(byte)
for unita_corrente in unita:
if dimensione < 1024 or unita_corrente == unita[-1]:
return f"{dimensione:.2f} {unita_corrente}"
dimensione /= 1024
def descrivi_permesse(mode):
"""Restituisce i permessi Unix in forma testuale."""
return stat.filemode(mode)
def analizza_file(percorso):
percorso = Path(percorso)
if not percorso.exists():
print(f"Il percorso non esiste: {percorso}")
return
if percorso.is_dir():
print(f"{percorso} è una directory.")
return
# stat() restituisce i metadati del file
informazioni = percorso.stat()
data_modifica = datetime.fromtimestamp(informazioni.st_mtime)
data_accesso = datetime.fromtimestamp(informazioni.st_atime)
data_creazione = datetime.fromtimestamp(informazioni.st_ctime)
print(f"Nome: {percorso.name}")
print(f"Percorso assoluto: {percorso.resolve()}")
print(f"Dimensione: {formatta_dimensione(informazioni.st_size)}")
print(f"Permessi: {descrivi_permesse(informazioni.st_mode)}")
print(f"Ultima modifica: {data_modifica:%Y-%m-%d %H:%M:%S}")
print(f"Ultimo accesso: {data_accesso:%Y-%m-%d %H:%M:%S}")
print(f"Modifica metadati/creazione: {data_creazione:%Y-%m-%d %H:%M:%S}")
# Controlli di accesso effettuati dal processo corrente
print(f"Leggibile: {os.access(percorso, os.R_OK)}")
print(f"Scrivibile: {os.access(percorso, os.W_OK)}")
print(f"Eseguibile: {os.access(percorso, os.X_OK)}")
def rendi_solo_lettura(percorso):
percorso = Path(percorso)
if not percorso.is_file():
raise FileNotFoundError(f"File non trovato: {percorso}")
# 0o444: lettura per proprietario, gruppo e altri utenti
os.chmod(percorso, 0o444)
print(f"Permessi aggiornati per: {percorso}")
def rendi_privato(percorso):
percorso = Path(percorso)
if not percorso.is_file():
raise FileNotFoundError(f"File non trovato: {percorso}")
# 0o600: lettura e scrittura solo per il proprietario
os.chmod(percorso, 0o600)
print(f"Il file è ora accessibile solo al proprietario: {percorso}")
if __name__ == "__main__":
file_demo = Path("dati_privati.txt")
# Creiamo un file di esempio
file_demo.write_text(
"Informazioni riservaten",
encoding="utf-8"
)
print("METADATI INIZIALI")
analizza_file(file_demo)
print("nRENDI IL FILE PRIVATO")
rendi_privato(file_demo)
analizza_file(file_demo)
print("nRENDI IL FILE DI SOLA LETTURA")
rendi_solo_lettura(file_demo)
analizza_file(file_demo)
# Ripristino utile per poter eliminare il file anche su sistemi Unix
os.chmod(file_demo, 0o600)
file_demo.unlink() Spiegazione
Leggere i metadati con stat()
Il metodo Path.stat() restituisce un oggetto con numerose proprietà. La proprietà st_size contiene la dimensione del file in byte. Le proprietà st_mtime, st_atime e st_ctime rappresentano rispettivamente la data dell’ultima modifica, dell’ultimo accesso e della modifica dei metadati.
Il significato di st_ctime non è identico su tutti i sistemi operativi. Su Unix indica generalmente l’ultima modifica degli attributi del file, mentre su Windows viene spesso interpretato come data di creazione. Per questo motivo è preferibile non considerarlo universalmente come una vera data di creazione.
Interpretare i permessi
La proprietà st_mode contiene informazioni sul tipo di elemento e sui suoi permessi. La funzione stat.filemode() trasforma questo valore in una stringa come -rw-------.
La prima posizione indica il tipo di elemento. Un trattino identifica normalmente un file regolare, mentre la lettera d identifica una directory. Le altre posizioni rappresentano i permessi di proprietario, gruppo e altri utenti:
- r: lettura;
- w: scrittura;
- x: esecuzione;
- -: permesso non disponibile.
Modificare i permessi con chmod()
La funzione os.chmod() modifica i permessi usando valori ottali. Il valore 0o600 consente al proprietario di leggere e modificare il file, impedendo l’accesso agli altri utenti. Il valore 0o444 rende il file leggibile, ma non modificabile, per proprietario, gruppo e altri utenti.
Questi valori sono particolarmente importanti quando si gestiscono file contenenti password, token, chiavi private o dati personali. Ad esempio, un file di configurazione riservato dovrebbe spesso avere permessi equivalenti a 0o600.
Controllare l’accesso con os.access()
os.access() permette di verificare se il processo corrente può leggere, scrivere o eseguire un file. Le costanti più comuni sono os.R_OK, os.W_OK e os.X_OK.
È importante ricordare che questo controllo non sostituisce la gestione delle eccezioni. Tra il momento del controllo e l’apertura effettiva del file, i permessi o il file stesso potrebbero cambiare. È quindi preferibile tentare direttamente l’operazione e gestire eventuali errori come PermissionError.
Best practice
- Usa
pathlib.Pathper rappresentare i percorsi: rende il codice più leggibile e portabile tra sistemi operativi. - Non modificare permessi indiscriminatamente: valori troppo permissivi possono esporre dati riservati.
- Proteggi i file sensibili: per configurazioni con segreti, considera permessi come
0o600. - Gestisci le eccezioni: usa blocchi
tryeexceptper gestireFileNotFoundError,PermissionErroreOSError. - Non fidarti soltanto di
os.access(): esegui l’operazione e gestisci l’errore, soprattutto in programmi concorrenti. - Considera le differenze tra sistemi operativi: i permessi ottali sono soprattutto significativi su sistemi Unix-like.
- Evita di usare
st_ctimecome data di creazione universale: il suo significato cambia tra Windows, Linux e macOS. - Non usare permessi di esecuzione senza motivo: un file di testo, ad esempio, normalmente non dovrebbe essere eseguibile.
Un controllo pratico può essere racchiuso in una funzione sicura:
from pathlib import Path
def leggi_file_sicuro(percorso):
try:
return Path(percorso).read_text(encoding="utf-8")
except FileNotFoundError:
print("Il file non esiste.")
except PermissionError:
print("Non hai i permessi necessari.")
except OSError as errore:
print(f"Errore del sistema: {errore}")
return None Riepilogo
La gestione dei metadati consente di analizzare e proteggere meglio i file. Con Path.stat() puoi ottenere dimensione e date importanti, con stat.filemode() puoi visualizzare i permessi in modo comprensibile e con os.chmod() puoi modificarli.
os.access() è utile per controlli preliminari, ma non deve sostituire la gestione delle eccezioni. Una soluzione robusta combina percorsi gestiti con pathlib, permessi minimi necessari e gestione esplicita degli errori.
