# Fichier: python_cheats/cheatsheets/sys.txt
# Cheatsheet sys Python - Guide Complet


[OK] INTRODUCTION

# sys fournit l'accès aux variables et fonctions liées à l'interpréteur Python
# Interaction avec le système et le runtime Python

import sys


[OK] ARGUMENTS LIGNE DE COMMANDE (sys.argv)


# sys.argv est une liste des arguments de ligne de commande
# argv[0] est le nom du script

# script.py
import sys

print(sys.argv)
# Exécution: python script.py arg1 arg2 arg3
# Sortie: ['script.py', 'arg1', 'arg2', 'arg3']

# Accéder aux arguments
script_name = sys.argv[0]
first_arg = sys.argv[1] if len(sys.argv) > 1 else None

# Exemple complet
if len(sys.argv) < 2:
    print("Usage: python script.py <filename>")
    sys.exit(1)

filename = sys.argv[1]
print(f"Processing {filename}")

# Parser arguments basique
def parse_args():
    args = {}
    for arg in sys.argv[1:]:
        if '=' in arg:
            key, value = arg.split('=', 1)
            args[key.lstrip('-')] = value
        else:
            args[arg.lstrip('-')] = True
    return args

# python script.py --name=Alice --verbose
# {'name': 'Alice', 'verbose': True}

# Meilleure pratique: utiliser argparse au lieu de sys.argv
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('filename')
args = parser.parse_args()


[OK] SORTIE DU PROGRAMME (sys.exit)


# Quitter avec code 0 (succès)
sys.exit(0)

# Quitter avec code d'erreur
sys.exit(1)

# Quitter avec message
sys.exit("Erreur: fichier introuvable")

# Dans try/except
try:
    # code
    pass
except Exception as e:
    print(f"Erreur: {e}", file=sys.stderr)
    sys.exit(1)

# Codes de sortie standards
# 0   : Succès
# 1   : Erreur générale
# 2   : Utilisation incorrecte
# 126 : Commande non exécutable
# 127 : Commande introuvable
# 128 : Code de sortie invalide
# 130 : Terminé par Ctrl+C

# Fonction avec sortie conditionnelle
def main():
    if not validate_input():
        sys.exit(1)
    process_data()
    sys.exit(0)

if __name__ == "__main__":
    main()


[OK] ENTRÉE/SORTIE STANDARD (stdin, stdout, stderr)


# sys.stdin - entrée standard
import sys

# Lire une ligne
line = sys.stdin.readline()

# Lire toutes les lignes
for line in sys.stdin:
    print(line.strip())

# Lire tout le contenu
content = sys.stdin.read()

# sys.stdout - sortie standard
sys.stdout.write("Hello World\n")
sys.stdout.flush()  # Forcer l'écriture

# sys.stderr - erreur standard
sys.stderr.write("Erreur: quelque chose a mal tourné\n")
print("Erreur", file=sys.stderr)

# Redirection
import sys
from io import StringIO

# Capturer stdout
old_stdout = sys.stdout
sys.stdout = StringIO()

print("Cette sortie est capturée")
output = sys.stdout.getvalue()

sys.stdout = old_stdout
print(f"Capturé: {output}")

# Redirection vers fichier
with open('output.txt', 'w') as f:
    sys.stdout = f
    print("Écrit dans le fichier")
    sys.stdout = sys.__stdout__  # Restaurer

# Tee (écrire vers stdout et fichier)
class Tee:
    def __init__(self, *files):
        self.files = files
    
    def write(self, data):
        for f in self.files:
            f.write(data)
    
    def flush(self):
        for f in self.files:
            f.flush()

with open('log.txt', 'w') as f:
    sys.stdout = Tee(sys.__stdout__, f)
    print("Écrit partout")


[OK] CHEMIN DE RECHERCHE (sys.path)


# sys.path est la liste des chemins de recherche de modules
print(sys.path)
# ['', '/usr/lib/python3.11', '/usr/lib/python3.11/lib-dynload', ...]

# Ajouter un chemin
sys.path.append('/chemin/vers/modules')
sys.path.insert(0, '/chemin/prioritaire')

# Supprimer un chemin
if '/some/path' in sys.path:
    sys.path.remove('/some/path')

# Ajouter chemin relatif
import os
current_dir = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, os.path.join(current_dir, 'lib'))

# Voir où un module est importé
import json
print(json.__file__)

# Context manager pour chemin temporaire
from contextlib import contextmanager

@contextmanager
def add_path(path):
    sys.path.insert(0, path)
    try:
        yield
    finally:
        sys.path.remove(path)

with add_path('/tmp/modules'):
    import custom_module


[OK] MODULES IMPORTÉS (sys.modules)


# sys.modules est un dict de tous les modules importés
print(sys.modules.keys())

# Vérifier si module est importé
if 'numpy' in sys.modules:
    print("NumPy déjà importé")

# Obtenir référence au module
json_module = sys.modules.get('json')

# Recharger un module (attention: peut causer des bugs)
if 'mymodule' in sys.modules:
    del sys.modules['mymodule']
import mymodule  # Réimporte

# Alternative avec importlib
import importlib
import mymodule
importlib.reload(mymodule)

# Lister tous les modules importés
for name, module in sys.modules.items():
    print(f"{name}: {module}")

# Taille du cache de modules
print(f"Modules chargés: {len(sys.modules)}")

# Forcer suppression d'un module
def unimport(module_name):
    if module_name in sys.modules:
        del sys.modules[module_name]


[OK] INFORMATIONS PLATEFORME


# Système d'exploitation
print(sys.platform)
# 'linux', 'darwin' (macOS), 'win32' (Windows)

# Détection OS
if sys.platform.startswith('linux'):
    print("Linux")
elif sys.platform == 'darwin':
    print("macOS")
elif sys.platform == 'win32':
    print("Windows")

# Version Python (tuple)
print(sys.version_info)
# sys.version_info(major=3, minor=11, micro=0, releaselevel='final', serial=0)

# Accéder aux composants
major = sys.version_info.major
minor = sys.version_info.minor
print(f"Python {major}.{minor}")

# Version Python (string)
print(sys.version)
# '3.11.0 (main, Oct 24 2022, 18:26:48) [GCC 11.3.0]'

# Vérifier version minimum
if sys.version_info < (3, 8):
    print("Python 3.8+ requis")
    sys.exit(1)

# Implementation Python
print(sys.implementation)
# namespace(name='cpython', version=...)

# Hexversion (comparaisons numériques)
print(sys.hexversion)
# 0x30b0000

if sys.hexversion >= 0x030A0000:  # Python 3.10+
    print("Python 3.10 ou supérieur")


[OK] TAILLE DES OBJETS (sys.getsizeof)


import sys

# Taille en bytes
x = [1, 2, 3, 4, 5]
print(sys.getsizeof(x))  # 104 bytes (par exemple)

# Différents types
print(sys.getsizeof(1))              # int
print(sys.getsizeof("hello"))        # str
print(sys.getsizeof([]))             # list vide
print(sys.getsizeof({}))             # dict vide
print(sys.getsizeof(set()))          # set vide

# Comparaison list vs tuple
lst = [1, 2, 3, 4, 5]
tpl = (1, 2, 3, 4, 5)
print(f"List: {sys.getsizeof(lst)}")
print(f"Tuple: {sys.getsizeof(tpl)}")

# Taille totale (récursif)
def get_total_size(obj, seen=None):
    """Calculer taille totale incluant les objets référencés"""
    size = sys.getsizeof(obj)
    if seen is None:
        seen = set()
    
    obj_id = id(obj)
    if obj_id in seen:
        return 0
    
    seen.add(obj_id)
    
    if isinstance(obj, dict):
        size += sum(get_total_size(k, seen) + get_total_size(v, seen) 
                   for k, v in obj.items())
    elif hasattr(obj, '__iter__') and not isinstance(obj, (str, bytes, bytearray)):
        size += sum(get_total_size(i, seen) for i in obj)
    
    return size

data = {'a': [1, 2, 3], 'b': {'c': 4, 'd': 5}}
print(get_total_size(data))


[OK] LIMITES ET CONFIGURATION


# Limite de récursion
print(sys.getrecursionlimit())  # 1000 par défaut

# Modifier limite
sys.setrecursionlimit(2000)

# Exemple avec récursion profonde
def deep_recursion(n):
    if n <= 0:
        return
    return deep_recursion(n - 1)

try:
    deep_recursion(1500)
except RecursionError:
    print("RecursionError!")

# Taille maximum int (Python 3: illimité)
# Python 2 seulement:
# print(sys.maxsize)  # Plus grand int possible
print(sys.maxsize)  # Valeur max sur plateforme

# Vérifier si 64-bit
is_64bit = sys.maxsize > 2**32
print(f"64-bit: {is_64bit}")

# Float info
print(sys.float_info)
# sys.float_info(max=1.7976931348623157e+308, ...)

# Int info (Python 3.1+)
print(sys.int_info)
# sys.int_info(bits_per_digit=30, sizeof_digit=4)


[OK] INFORMATIONS RUNTIME


# Flags de l'interpréteur
print(sys.flags)
# sys.flags(debug=0, inspect=0, interactive=0, optimize=0, ...)

# Vérifier mode debug
if sys.flags.debug:
    print("Mode debug activé")

# Vérifier optimisation
if sys.flags.optimize:
    print("Optimisation activée")

# Encodage par défaut
print(sys.getdefaultencoding())  # 'utf-8'

# Encodage filesystem
print(sys.getfilesystemencoding())  # 'utf-8' (Linux/Mac), 'mbcs' (Windows)

# Exécutable Python
print(sys.executable)
# '/usr/bin/python3.11'

# Préfixe installation
print(sys.prefix)
# '/usr' ou chemin venv

# Base exec prefix
print(sys.base_prefix)  # Python installation originale

# Dans un venv?
in_venv = sys.prefix != sys.base_prefix
print(f"Dans venv: {in_venv}")

# Copyright
print(sys.copyright)

# API version Python C
print(sys.api_version)


[OK] EXCEPTIONS ET TRACEBACK


# Dernière exception
try:
    1 / 0
except:
    exc_type, exc_value, exc_traceback = sys.exc_info()
    print(f"Type: {exc_type}")
    print(f"Value: {exc_value}")
    print(f"Traceback: {exc_traceback}")

# Hook d'exception personnalisé
def custom_excepthook(exc_type, exc_value, exc_traceback):
    print(f"Erreur capturée: {exc_value}")
    # Logger, envoyer à Sentry, etc.

sys.excepthook = custom_excepthook

# Exception non catchée
# raise ValueError("Test")  # Utilisera custom_excepthook

# Restaurer hook original
sys.excepthook = sys.__excepthook__

# Display hook (pour REPL)
def custom_displayhook(value):
    if value is not None:
        print(f"Résultat: {value}")

sys.displayhook = custom_displayhook

# Frame actuel (introspection)
import sys

def get_current_frame_info():
    frame = sys._getframe()
    return {
        'function': frame.f_code.co_name,
        'filename': frame.f_code.co_filename,
        'line': frame.f_lineno
    }


[OK] COMPTEURS DE RÉFÉRENCES


# Nombre de références à un objet
x = [1, 2, 3]
print(sys.getrefcount(x))  # Au moins 2 (x + argument fonction)

y = x
print(sys.getrefcount(x))  # Augmente

del y
print(sys.getrefcount(x))  # Diminue

# Attention: getrefcount ajoute +1 temporairement
# pour son propre argument


[OK] AUDIT HOOKS (Python 3.8+)


# Hook d'audit pour sécurité
def audit_hook(event, args):
    print(f"Event: {event}, Args: {args}")

sys.addaudithook(audit_hook)

# Maintenant toutes les opérations sensibles sont auditées:
# - open()
# - compile()
# - exec()
# - import
# - etc.

# Exemple pratique: bloquer certaines opérations
def security_hook(event, args):
    if event == 'open':
        filename = args[0]
        if '/etc/passwd' in filename:
            raise PermissionError("Accès refusé")

sys.addaudithook(security_hook)


[OK] STDIN/STDOUT ENCODING


# Encodage de stdin
print(sys.stdin.encoding)  # 'utf-8'

# Changer encodage (pas recommandé)
import io
sys.stdin = io.TextIOWrapper(
    sys.stdin.buffer,
    encoding='latin-1'
)

# Vérifier si stdout est un terminal
if sys.stdout.isatty():
    print("Sortie vers terminal")
else:
    print("Sortie redirigée (pipe/fichier)")

# Désactiver buffering
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', buffering=1)

# Ou via Python:
# python -u script.py  # Unbuffered


[OK] BYTEORDER & ARCHITECTURE


# Byte order de la plateforme
print(sys.byteorder)  # 'little' ou 'big'

# Little endian (x86, x86_64)
# Big endian (certains ARM, SPARC)

# Taille des pointeurs (32 vs 64 bit)
import struct
pointer_size = struct.calcsize('P') * 8
print(f"Architecture: {pointer_size}-bit")

# Maximum unicode
print(sys.maxunicode)  # 1114111 (0x10FFFF)


[OK] HASH RANDOMIZATION


# Seed de hash (sécurité)
print(sys.hash_info)
# sys.hash_info(width=64, modulus=2305843009213693951, ...)

# Hash randomization activé par défaut (Python 3.3+)
# Empêche les attaques par collision de hash


[OK] PROFILING & TRACING


# Set trace function
def trace_calls(frame, event, arg):
    if event == 'call':
        code = frame.f_code
        print(f"Appel: {code.co_filename}:{frame.f_lineno} {code.co_name}")
    return trace_calls

sys.settrace(trace_calls)

# Exécuter du code tracé
def my_function():
    x = 1 + 1
    return x

my_function()

# Désactiver tracing
sys.settrace(None)

# Profile function (pour performance)
def profile_func(frame, event, arg):
    if event == 'call':
        # Mesurer temps, compter appels, etc.
        pass
    return profile_func

sys.setprofile(profile_func)


[OK] VARIABLES PRIVÉES UTILES


# Stdout/stderr/stdin originaux (avant redirection)
original_stdout = sys.__stdout__
original_stderr = sys.__stderr__
original_stdin = sys.__stdin__

# Restaurer après redirection
sys.stdout = sys.__stdout__
sys.stderr = sys.__stderr__

# Display/except hooks originaux
sys.displayhook = sys.__displayhook__
sys.excepthook = sys.__excepthook__


[OK] EXEMPLES PRATIQUES


# 1. Script CLI robuste
import sys

def main():
    if len(sys.argv) < 2:
        print("Usage: script.py <file>", file=sys.stderr)
        return 1
    
    filename = sys.argv[1]
    
    try:
        with open(filename) as f:
            content = f.read()
            print(content)
        return 0
    except FileNotFoundError:
        print(f"Erreur: {filename} introuvable", file=sys.stderr)
        return 1
    except Exception as e:
        print(f"Erreur: {e}", file=sys.stderr)
        return 2

if __name__ == "__main__":
    sys.exit(main())


# 2. Détection environnement
def detect_environment():
    info = {
        'python_version': f"{sys.version_info.major}.{sys.version_info.minor}",
        'platform': sys.platform,
        'is_64bit': sys.maxsize > 2**32,
        'in_virtualenv': sys.prefix != sys.base_prefix,
        'executable': sys.executable,
    }
    return info


# 3. Logger vers fichier et console
class DualLogger:
    def __init__(self, filename):
        self.terminal = sys.stdout
        self.log = open(filename, 'w')
    
    def write(self, message):
        self.terminal.write(message)
        self.log.write(message)
    
    def flush(self):
        self.terminal.flush()
        self.log.flush()

sys.stdout = DualLogger('output.log')


# 4. Progress bar compatible pipe
def progress_bar(current, total):
    if sys.stdout.isatty():
        percent = current / total * 100
        bar = '█' * int(percent / 2) + '░' * (50 - int(percent / 2))
        print(f'\r[{bar}] {percent:.1f}%', end='', flush=True)
    else:
        # Pas de progress bar si redirigé
        if current % (total // 10) == 0:
            print(f"Progress: {current}/{total}")


# 5. Gestion mémoire
def check_memory_usage():
    """Vérifier si l'utilisation mémoire est raisonnable"""
    import gc
    
    # Forcer garbage collection
    gc.collect()
    
    # Compter objets
    objects = gc.get_objects()
    
    type_counts = {}
    for obj in objects:
        t = type(obj).__name__
        type_counts[t] = type_counts.get(t, 0) + 1
    
    # Top 10 types
    for t, count in sorted(type_counts.items(), 
                           key=lambda x: x[1], 
                           reverse=True)[:10]:
        print(f"{t}: {count}")


# 6. Context manager pour stdout
from contextlib import contextmanager

@contextmanager
def redirect_stdout(new_target):
    old_target = sys.stdout
    sys.stdout = new_target
    try:
        yield new_target
    finally:
        sys.stdout = old_target

with redirect_stdout(open('output.txt', 'w')):
    print("Écrit dans fichier")


# 7. Timeout pour stdin
import sys
import select

def read_with_timeout(timeout=5):
    """Lire stdin avec timeout (Unix seulement)"""
    if sys.platform == 'win32':
        return input("Entrée: ")
    
    print("Entrée (timeout 5s): ", end='', flush=True)
    ready, _, _ = select.select([sys.stdin], [], [], timeout)
    
    if ready:
        return sys.stdin.readline().strip()
    else:
        print("\nTimeout!")
        return None


# 8. Vérifier si script ou module
def is_main_script():
    """Vérifier si c'est le script principal"""
    import __main__
    return hasattr(__main__, '__file__')


# 9. Analyser performance import
import time

class ImportTimer:
    def __init__(self):
        self.times = {}
    
    def __call__(self, name, *args):
        start = time.time()
        # Import réel ici
        elapsed = time.time() - start
        self.times[name] = elapsed
        return elapsed

# sys.meta_path.insert(0, ImportTimer())


# 10. Exception globale avec contexte
def setup_global_exception_handler():
    def handle_exception(exc_type, exc_value, exc_traceback):
        if issubclass(exc_type, KeyboardInterrupt):
            sys.__excepthook__(exc_type, exc_value, exc_traceback)
            return
        
        import traceback
        print("=" * 60)
        print("ERREUR NON GÉRÉE")
        print("=" * 60)
        print(f"Type: {exc_type.__name__}")
        print(f"Message: {exc_value}")
        print("\nTraceback:")
        traceback.print_tb(exc_traceback)
        print("=" * 60)
        
        # Logger, envoyer à Sentry, etc.
    
    sys.excepthook = handle_exception


[OK] COMPATIBILITÉ PYTHON 2 vs 3


# Détection version
if sys.version_info[0] >= 3:
    # Python 3
    input_func = input
else:
    # Python 2
    input_func = raw_input

# Print function
from __future__ import print_function  # Python 2

# Integer division
if sys.version_info < (3,):
    range = xrange  # Python 2


[OK] BONNES PRATIQUES


# [OK] Toujours vérifier sys.argv avant d'y accéder
# [OK] Utiliser sys.exit() pour codes de sortie propres
# [OK] Préférer argparse à sys.argv pour CLI complexes
# [OK] Utiliser sys.stderr pour erreurs/warnings
# [OK] Sauvegarder sys.__stdout__ avant redirection
# [OK] Vérifier sys.version_info pour compatibilité
# [OK] Utiliser sys.platform pour code multi-plateforme
# [OK] Flush sys.stdout si besoin de sortie immédiate

# [X] Ne jamais modifier sys.path en production
# [X] Ne pas augmenter recursionlimit sans raison
# [X] Attention à getsizeof (ne compte pas références)
# [X] Ne pas rediriger stdout sans le restaurer
# [X] sys.modules modification peut casser imports


[OK] DEBUGGING & INTROSPECTION


# Afficher toutes les variables sys importantes
def print_sys_info():
    info = {
        'argv': sys.argv,
        'platform': sys.platform,
        'version': sys.version,
        'path': sys.path,
        'executable': sys.executable,
        'prefix': sys.prefix,
        'base_prefix': sys.base_prefix,
        'maxsize': sys.maxsize,
        'recursionlimit': sys.getrecursionlimit(),
        'defaultencoding': sys.getdefaultencoding(),
        'stdin_encoding': sys.stdin.encoding,
    }
    
    for key, value in info.items():
        print(f"{key}: {value}")


[OK] RESSOURCES


# Documentation: https://docs.python.org/3/library/sys.html
# PEP 432: Restructuring the CPython startup sequence
# PEP 587: Python Initialization Configuration
# sys module source: Lib/sys.py (limité, surtout C)