Résumé du système
Toutes les infos en une ligne de code.
from simplesuit import sys as ss
for nom, valeur in ss.summary().items():
print(f"{nom:>10} : {valeur}")Infos système : batterie, CPU, RAM, disque, réseau, son
Inclus dans simplesuit : installe simplesuit pour l'avoir.
import simplesuit.sys · PyPI : simplesuit · Python 3.9+ · dépend de psutil
Les infos de ta machine en une ligne chacune : batterie, processeur, mémoire, disque, réseau, son, système… Toujours dans un format prêt à afficher ("6.6 Go", "7h43"). Et une saisie de mot de passe avec des *.
from simplesuit import sys as ss
for nom, valeur in ss.summary().items():
print(f"{nom:<10} {valeur}")user izac
hostname MacBook-de-Izac
os macOS 27.0.1
python 3.9.6
cpu 12.4% (8 cores, arm64)
ram 63.2% (6.6 Go / 16.0 Go)
disk 33.4% (306.8 Go free / 460.4 Go)
battery 66% (plugged)
time_left -
uptime 26h12
processes 412
local_ip 192.168.1.42
online True
volume 31%| Thème | Fonctions | Exemple |
|---|---|---|
| Batterie | battery(), plugged(), time_left(), time_left_text() | 66, True, 27780, "7h43" |
| Processeur | cpu(), cpu_cores(), cpu_type() | 12.4, 8, "arm64" |
| Mémoire | ram(), ram_used(), ram_total() | 63.2, "6.6 Go", "16.0 Go" |
| Disque | disk(), disk_free(), disk_total() | 33.4, "306.8 Go", "460.4 Go" |
| Réseau | online(), local_ip(), public_ip(), wifi_connected(), wifi(), net_sent(), net_received() | True, "192.168.1.42" |
| Système | hostname(), user(), os_name(), os_version(), python_version(), python_path(), uptime(), uptime_text(), processes(), home() | "macOS", "26h12" |
| Son (macOS) | volume(), muted() | 31, False |
| Texte | human_size(octets), human_time(secondes) | "1.5 Ko", "7h43" |
Les pourcentages sont des nombres (pour comparer : if ss.ram() > 90:), les tailles et les durées sont des textes (pour afficher).
None veut dire « pas disponible »
Pas de batterie (ordi fixe), volume inconnu (pas sur Mac), hors ligne pour public_ip()… La fonction renvoie None au lieu de planter. Pense à ss.battery() or 0 avant un calcul.
ss.password(texte) lit un mot de passe en affichant un * par lettre tapée (au lieu de rien du tout avec getpass). Le retour arrière efface, Ctrl+U efface tout.
from simplesuit import sys as ss
try:
mdp = ss.password("password > ")
except (KeyboardInterrupt, EOFError): # Ctrl+C ou Ctrl+D
exit("\nannulé")password > ********mask="•" pour changer le caractère : ss.password("PIN : ", mask="•").echo 1234 | python3 main.py), il lit simplement la ligne.Le mot de passe reste en mémoire en clair
password() cache la saisie à l'écran, c'est tout. Ne l'affiche pas, ne l'écris pas dans un fichier : envoie-le (chiffré, avec capy par exemple) et stocke seulement son hash côté serveur.
cpu() prend 0,3 seconde
Pour mesurer l'utilisation du processeur, il faut comparer deux instants : cpu() attend 0,3 s. Dans une boucle qui se rafraîchit, c'est très bien ; dans un programme qui doit répondre vite, appelle-le une seule fois.
public_ip() demande internet
Elle interroge un site extérieur : un peu lente, et None hors ligne. C'est pour ça qu'elle n'est pas dans summary().
Le nom du wifi sur macOS récent
Depuis macOS 14, Apple cache le nom du réseau aux programmes pour protéger la vie privée : ss.wifi() renvoie souvent None. ss.wifi_connected() marche toujours.
Un tableau de bord qui se rafraîchit
Efface l'écran avec print("\033[2J\033[H", end=""), affiche tes infos, attends une seconde, recommence. Voir l'exemple « Mini tableau de bord ».
C'est la bibliothèque de référence pour lire CPU, RAM, disque et batterie sur Mac, Linux et Windows. Elle s'installe toute seule avec simplesuit, et seul simplesuit.sys l'utilise.
Toutes les infos en une ligne de code.
from simplesuit import sys as ss
for nom, valeur in ss.summary().items():
print(f"{nom:>10} : {valeur}")sys donne les chiffres, colors et icons les mettent en forme.
from simplesuit import colors as sc
from simplesuit import icons as si
from simplesuit import sys as ss
niveau = ss.battery()
if niveau is None:
print("Pas de batterie sur cet ordinateur")
else:
couleur = "lime" if niveau > 50 else "orange" if niveau > 20 else "red"
barre = "█" * (niveau // 5) + "░" * (20 - niveau // 5)
reste = ss.time_left_text() or "-"
print(si.battery(niveau, ss.plugged()), sc.color(barre, couleur), f"{niveau} % (reste {reste})")Connexion, adresses IP, mémoire, disque et temps depuis le démarrage.
from simplesuit import sys as ss
print("Connecté à internet :", ss.online())
print("IP locale :", ss.local_ip())
print("IP publique :", ss.public_ip()) # Demande internet (≈ 1 s)
print("Données reçues :", ss.net_received())
print(f"Processeur : {ss.cpu()} % ({ss.cpu_cores()} cœurs, {ss.cpu_type()})")
print(f"Mémoire : {ss.ram_used()} / {ss.ram_total()}")
print(f"Disque : {ss.disk_free()} libres sur {ss.disk_total()}")
print("Allumé depuis :", ss.uptime_text())password() affiche une * par lettre tapée. Retour arrière efface, Ctrl+U efface tout, Ctrl+C ou Ctrl+D annulent (KeyboardInterrupt / EOFError).
from simplesuit import colors as sc
from simplesuit import sys as ss
try:
mdp = ss.password("password > ")
confirmation = ss.password("encore une fois > ", mask="•")
except (KeyboardInterrupt, EOFError):
exit("\nannulé")
if mdp != confirmation:
exit(sc.red("Les deux mots de passe sont différents"))
print(sc.green(f"OK, {len(mdp)} caractères")) # On n'affiche jamais le mot de passe lui-mêmeToutes les secondes : on efface l'écran, puis on affiche les infos avec des icônes et des couleurs. Ctrl+C pour quitter.
import time
from simplesuit import colors as sc
from simplesuit import icons as si
from simplesuit import sys as ss
# Une barre de progression colorée selon le niveau (vert, orange, rouge)
def barre(pourcentage, largeur=20):
plein = round(pourcentage / 100 * largeur)
couleur = "mint" if pourcentage < 60 else "orange" if pourcentage < 85 else "tomato"
return sc.color("█" * plein, couleur) + sc.color("░" * (largeur - plein), "dimgray")
try:
while True:
cpu, ram, disque = ss.cpu(), ss.ram(), ss.disk() # cpu() attend 0,3 s : c'est notre pause
print("\033[2J\033[H", end="") # Effacer l'écran, curseur en haut
print(sc.gradient(f" {ss.hostname()} ", "cyan", "magenta", styles="bold"), "\n")
print(f"{si.cpu} CPU {barre(cpu)} {cpu:5.1f} %")
print(f"{si.memory} RAM {barre(ram)} {ram:5.1f} % {ss.ram_used()}")
print(f"{si.disk} Disque {barre(disque)} {disque:5.1f} % {ss.disk_free()} libres")
if ss.battery() is not None:
print(f"{si.battery(ss.battery(), bool(ss.plugged()))} Batterie {ss.battery()} %")
print(sc.color(f"\nAllumé depuis {ss.uptime_text()} · Ctrl+C pour quitter", "gray"))
time.sleep(0.7)
except KeyboardInterrupt:
print()file_icon() choisit l'icône selon le type : dossier, .py, .json, .md, image, musique… Les dossiers en bleu et en premier, les tailles alignées à droite.
import os
from simplesuit import colors as sc
from simplesuit import icons as si
from simplesuit import sys as ss
noms = sorted(os.listdir("."), key=lambda n: (not os.path.isdir(n), n.lower()))
for nom in noms:
if nom.startswith("."):
continue
if os.path.isdir(nom):
print(sc.color(f"{si.file_icon(nom)} {nom}/", "cornflowerblue", styles="bold"))
else:
taille = ss.human_size(os.path.getsize(nom))
print(f"{si.file_icon(nom)} {nom:<30} {sc.color(f'{taille:>9}', 'gray')}")human_size(octets)Transformer un nombre d'octets en texte lisible
Ex1536 -> "1.5 Ko" / 8589934592 -> "8.0 Go"
def human_size(octets):
for unit in ["o", "Ko", "Mo", "Go", "To"]:
if octets < 1024:
return f"{octets:.1f} {unit}"
octets /= 1024
return f"{octets:.1f} Po"human_time(seconds)Transformer des secondes en texte lisible
Ex27780 -> "7h43" / 300 -> "5min"
def human_time(seconds):
hours, rest = divmod(int(seconds), 3600)
minutes = rest // 60
if hours:
return f"{hours}h{minutes:02d}"
return f"{minutes}min"battery()Pourcentage de batterie (ex : 64), None si pas de batterie
def battery():
bat = psutil.sensors_battery()
return bat.percent if bat else Noneplugged()Chargeur branché ? (True / False), None si pas de batterie
def plugged():
bat = psutil.sensors_battery()
return bat.power_plugged if bat else Nonetime_left()Temps restant en secondes, None si inconnu ou si branché
def time_left():
bat = psutil.sensors_battery()
# secsleft est négatif quand le temps est inconnu ou illimité
if bat is None or bat.secsleft < 0:
return None
return bat.secslefttime_left_text()Temps restant en texte (ex : "7h43"), None si inconnu
def time_left_text():
seconds = time_left()
return human_time(seconds) if seconds is not None else Nonecpu()Utilisation du processeur en % (ex : 12.5)
Mesure pendant 0.3 seconde : la 1ère mesure instantanée de psutil vaut toujours 0
def cpu():
return psutil.cpu_percent(interval=0.3)cpu_cores()Nombre de cœurs (ex : 8)
def cpu_cores():
return psutil.cpu_count()cpu_type()Type de processeur (ex : "arm64" pour Apple Silicon, "x86_64" pour Intel)
def cpu_type():
return platform.machine()ram()RAM utilisée en % (ex : 63.2)
def ram():
return psutil.virtual_memory().percentram_used()RAM utilisée en texte (ex : "6.6 Go")
On calcule total - disponible : c'est ce qui correspond au pourcentage de ram()
def ram_used():
mem = psutil.virtual_memory()
return human_size(mem.total - mem.available)ram_total()RAM totale en texte (ex : "16.0 Go")
def ram_total():
return human_size(psutil.virtual_memory().total)disk(path='/')Disque utilisé en % (ex : 22.1)
On calcule à partir de l'espace libre : sur Mac, le disque est découpé en plusieurs
volumes qui partagent la même place, et le % de psutil ne compte que le volume "/"
def disk(path="/"):
usage = psutil.disk_usage(path)
return round((usage.total - usage.free) / usage.total * 100, 1)disk_free(path='/')Espace libre en texte (ex : "120.4 Go")
def disk_free(path="/"):
return human_size(psutil.disk_usage(path).free)disk_total(path='/')Taille totale du disque en texte (ex : "460.4 Go")
def disk_total(path="/"):
return human_size(psutil.disk_usage(path).total)online()Connecté à internet ? (True / False)
def online():
try:
socket.create_connection(("1.1.1.1", 53), timeout=2).close()
return True
except OSError:
return Falselocal_ip()Adresse IP locale, sur ton réseau (ex : "192.168.1.42")
def local_ip():
try:
# Astuce : on "vise" une adresse externe sans rien envoyer, juste pour savoir quelle IP on utilise
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
s.connect(("1.1.1.1", 80))
return s.getsockname()[0]
except OSError:
return Nonepublic_ip()Adresse IP publique, vue depuis internet (None si hors ligne)
def public_ip():
try:
with urllib.request.urlopen("https://api.ipify.org", timeout=3) as response:
return response.read().decode()
except OSError:
return Nonewifi_connected()Wifi activé et connecté ? (True / False)
def wifi_connected():
return _run(["ipconfig", "getifaddr", "en0"]) != ""wifi()Nom du réseau wifi (ex : "Livebox-1234")
⚠️ Depuis macOS 14, Apple cache ce nom pour la vie privée -> renvoie souvent None
def wifi():
for line in _run(["ipconfig", "getsummary", "en0"]).splitlines():
line = line.strip()
if line.startswith("SSID :"):
name = line.split(":", 1)[1].strip()
return None if name == "<redacted>" else name
return Nonenet_sent()Données envoyées depuis le démarrage, en texte (ex : "902.5 Mo")
def net_sent():
return human_size(psutil.net_io_counters().bytes_sent)net_received()Données reçues depuis le démarrage, en texte (ex : "12.3 Go")
def net_received():
return human_size(psutil.net_io_counters().bytes_recv)hostname()Nom de l'ordinateur (ex : "MacBook-de-Izac")
def hostname():
return platform.node().removesuffix(".local")user()Nom de l'utilisateur (ex : "izac")
def user():
return getpass.getuser()os_name()Système d'exploitation (ex : "macOS")
def os_name():
names = {"Darwin": "macOS", "Windows": "Windows", "Linux": "Linux"}
return names.get(platform.system(), platform.system())os_version()Version du système (ex : "27.0.1")
def os_version():
if platform.system() == "Darwin":
return platform.mac_ver()[0]
return platform.release()python_version()Version de Python (ex : "3.9.6")
def python_version():
return platform.python_version()python_path()Chemin du Python utilisé
def python_path():
return sys.executableuptime()Temps depuis le démarrage du Mac, en secondes
def uptime():
return time.time() - psutil.boot_time()uptime_text()Temps depuis le démarrage, en texte (ex : "26h12")
def uptime_text():
return human_time(uptime())processes()Nombre de programmes en cours
def processes():
return len(psutil.pids())home()Dossier perso (ex : "/Users/izac")
def home():
return os.path.expanduser("~")volume()Volume du son en % (ex : 31), None si inconnu
def volume():
value = _run(["osascript", "-e", "output volume of (get volume settings)"])
return int(value) if value.isdigit() else Nonemuted()Son coupé ? (True / False)
def muted():
return _run(["osascript", "-e", "output muted of (get volume settings)"]) == "true"password(prompt='password > ', mask='*')Demander un mot de passe : chaque lettre tapée s'affiche en "*" (au lieu de rien avec getpass)
Retour arrière efface, Ctrl+C -> KeyboardInterrupt, Ctrl+D (champ vide) -> EOFError
Exmdp = password("password > ") / password("PIN : ", mask="•")
def password(prompt="password > ", mask="*"):
if not sys.stdin.isatty(): # Entrée redirigée (ex : echo 1234 | python3 main.py)
return input(prompt)
print(prompt, end="", flush=True)
chars = []
try:
for char in _keys():
if char in ("\r", "\n"): # Entrée
break
if char == "\x03": # Ctrl+C
raise KeyboardInterrupt
if char == "\x04" and not chars: # Ctrl+D sur un champ vide
raise EOFError
if char in ("\x7f", "\b"): # Retour arrière
if chars:
chars.pop()
print("\b \b", end="", flush=True)
elif char == "\x15": # Ctrl+U : tout effacer
print("\b \b" * len(chars), end="", flush=True)
chars.clear()
elif char.isprintable(): # Ignore Tab, Échap...
chars.append(char)
print(mask, end="", flush=True)
finally:
print() # Ici le terminal est déjà remis normal -> vrai retour à la ligne
return "".join(chars)summary()Toutes les infos dans un dict (pratique pour une commande "sysinfo")
public_ip n'est pas inclus car il demande internet et prend du temps
def summary():
bat, vol = battery(), volume() # Une seule mesure chacun (au lieu de 2-3 appels)
return {
"user": user(),
"hostname": hostname(),
"os": f"{os_name()} {os_version()}",
"python": python_version(),
"cpu": f"{cpu()}% ({cpu_cores()} cores, {cpu_type()})",
"ram": f"{ram()}% ({ram_used()} / {ram_total()})",
"disk": f"{disk()}% ({disk_free()} free / {disk_total()})",
"battery": f"{bat}%" + (" (plugged)" if plugged() else "") if bat is not None else "none",
"time_left": time_left_text() or "-",
"uptime": uptime_text(),
"processes": processes(),
"local_ip": local_ip() or "-",
"online": online(),
"volume": f"{vol}%" + (" (muted)" if muted() else "") if vol is not None else "-",
}Rien ne correspond.
↑↓ choisir · Entrée ouvrir