# Configuration automatique du kernel WSL pour OpenSpiel (Windows uniquement)
#
# IMPORTANT: Ce code cree un wrapper bash qui gere la conversion des chemins Windows->Linux.
# Le probleme principal est que VSCode passe les chemins avec des backslashes qui sont
# "consommes" par le shell WSL avant d'arriver au script Python.
#
# Solution: Un wrapper bash avec regex qui reconstruit le chemin.
# Voir install_wsl_kernel.md pour plus de details.
import subprocess
import json
import os
from pathlib import Path
class WSLOpenSpielManager:
"""Gestionnaire du kernel Python WSL avec OpenSpiel"""
def __init__(self):
self.is_windows = platform.system() == "Windows"
self.kernel_name = "gametheory-wsl"
self.kernel_path = Path(os.environ.get("APPDATA", "")) / "jupyter" / "kernels" / self.kernel_name
self.venv_path = "~/.gametheory-venv"
self.wrapper_path = "~/.gametheory-kernel-wrapper.sh"
def _safe_decode(self, data: bytes) -> str:
"""Decode bytes safely"""
return data.decode('utf-8', errors='replace')
def _run_wsl_command(self, cmd: str, timeout: int = 30) -> tuple:
"""Execute une commande dans WSL et retourne (success, stdout, stderr)"""
try:
result = subprocess.run(
["wsl.exe", "-d", "Ubuntu", "--", "bash", "-c", cmd],
capture_output=True, timeout=timeout
)
return (
result.returncode == 0,
self._safe_decode(result.stdout).strip(),
self._safe_decode(result.stderr).strip()
)
except subprocess.TimeoutExpired:
return (False, "", "Timeout")
except Exception as e:
return (False, "", str(e))
def check_wsl_available(self) -> dict:
"""Verifie si WSL est disponible"""
if not self.is_windows:
return {"available": False, "reason": "Non Windows - WSL non necessaire"}
try:
result = subprocess.run(["wsl.exe", "--status"], capture_output=True, timeout=10)
if result.returncode == 0:
return {"available": True}
return {"available": False, "reason": "WSL non configure"}
except FileNotFoundError:
return {"available": False, "reason": "WSL non installe"}
except Exception as e:
return {"available": False, "reason": str(e)}
def check_wsl_openspiel(self) -> dict:
"""Verifie si OpenSpiel est installe dans WSL"""
success, stdout, stderr = self._run_wsl_command(
f"source {self.venv_path}/bin/activate 2>/dev/null && "
"python3 -c \"import pyspiel; print(len(pyspiel.registered_names()))\"",
timeout=30
)
if success and stdout.isdigit():
return {"installed": True, "games_count": int(stdout)}
return {"installed": False, "reason": stderr or "OpenSpiel non installe dans WSL"}
def install_openspiel_wsl(self) -> bool:
"""Installe OpenSpiel et toutes les dependances GameTheory dans WSL"""
print("[*] Installation complete dans WSL (peut prendre quelques minutes)...")
# Installation systeme + venv + toutes les dependances de la serie
commands = f"""
sudo apt update -qq && sudo apt install -y python3-pip python3-venv python3-full -qq
if [ ! -d {self.venv_path} ]; then
python3 -m venv {self.venv_path}
fi
source {self.venv_path}/bin/activate
pip install --upgrade pip -q
pip install open_spiel nashpy numpy scipy matplotlib networkx seaborn pandas tqdm pysat ipykernel -q
python3 -c "import pyspiel; print(f'OpenSpiel OK: {{len(pyspiel.registered_names())}} jeux')"
python3 -c "from pysat.solvers import Glucose3; print('PySAT OK')"
"""
success, stdout, stderr = self._run_wsl_command(commands, timeout=300)
if success and ("OK" in stdout):
print(f"[+] {stdout}")
return True
print(f"[!] Echec: {stderr[:200] if stderr else 'erreur inconnue'}")
return False
def create_wrapper_script(self, wsl_user: str) -> bool:
"""Cree le wrapper bash dans WSL qui gere la conversion des chemins"""
print("[*] Creation du wrapper script...")
venv_expanded = self.venv_path.replace("~", f"/home/{wsl_user}")
wrapper_expanded = self.wrapper_path.replace("~", f"/home/{wsl_user}")
# Script wrapper qui gere:
# 1. Tilde notation: ~\\AppData\\...
# 2. Backslashes strippes: c:UsersjsboiAppDataRoamingjupyterruntime...
# 3. Chemins Windows normaux: C:\\Users\\...
wrapper_content = f'''#!/bin/bash
# Kernel wrapper for WSL - handles Windows path conversion
# Handles: tilde notation, stripped backslashes, normal Windows paths
LOGFILE="/tmp/kernel-wrapper.log"
echo "=== Kernel wrapper started ===" > "$LOGFILE"
echo "Args: $@" >> "$LOGFILE"
ARGS=()
NEXT_IS_CONN=false
for arg in "$@"; do
if [ "$NEXT_IS_CONN" = true ]; then
echo "Original path: $arg" >> "$LOGFILE"
# Case 1: Tilde notation (~\\AppData\\...)
if [[ "$arg" == ~* ]]; then
WIN_HOME=$(cmd.exe /c "echo %USERPROFILE%" 2>/dev/null | tr -d "\\r\\n")
arg="${{WIN_HOME}}${{arg:1}}"
echo "After tilde expansion: $arg" >> "$LOGFILE"
fi
# Case 2: Backslashes stripped (c:UsersjsboiAppDataRoamingjupyterruntime...)
if [[ "$arg" =~ ^c:Users([a-zA-Z0-9_]+)AppDataRoamingjupyterruntime(.*)$ ]]; then
USERNAME="${{BASH_REMATCH[1]}}"
FILENAME="${{BASH_REMATCH[2]}}"
arg="C:\\\\Users\\\\${{USERNAME}}\\\\AppData\\\\Roaming\\\\jupyter\\\\runtime\\\\${{FILENAME}}"
echo "Reconstructed path: $arg" >> "$LOGFILE"
fi
# Convert Windows path to Linux path
if [[ "$arg" == *":"* ]] || [[ "$arg" == *"\\\\"* ]]; then
LINUX_PATH=$(wslpath -u "$arg" 2>/dev/null)
if [ -n "$LINUX_PATH" ]; then
arg="$LINUX_PATH"
fi
fi
echo "Final path: $arg" >> "$LOGFILE"
ARGS+=("$arg")
NEXT_IS_CONN=false
elif [ "$arg" = "-f" ]; then
ARGS+=("$arg")
NEXT_IS_CONN=true
else
ARGS+=("$arg")
fi
done
echo "Final args: ${{ARGS[@]}}" >> "$LOGFILE"
echo "Launching ipykernel..." >> "$LOGFILE"
export PATH="{venv_expanded}/bin:$PATH"
cd ~
exec {venv_expanded}/bin/python3 -m ipykernel_launcher "${{ARGS[@]}}"
'''
# Ecrire le script via fichier temporaire (evite les problemes d'echappement)
import tempfile
with tempfile.NamedTemporaryFile(mode='w', suffix='.sh', delete=False, newline='\n') as f:
f.write(wrapper_content)
temp_path = f.name
# Copier vers WSL
wsl_temp = f"/mnt/{temp_path[0].lower()}/{temp_path[3:].replace(chr(92), '/')}"
success, _, stderr = self._run_wsl_command(
f"cp '{wsl_temp}' {wrapper_expanded} && chmod +x {wrapper_expanded}"
)
# Nettoyer
os.unlink(temp_path)
if success:
# Verifier la syntaxe
success, _, stderr = self._run_wsl_command(f"bash -n {wrapper_expanded}")
if success:
print(f"[+] Wrapper cree: {wrapper_expanded}")
return True
print(f"[!] Erreur syntaxe: {stderr}")
else:
print(f"[!] Echec copie: {stderr}")
return False
def install_kernel(self) -> bool:
"""Installe le kernel Jupyter pour WSL avec wrapper script"""
print("[*] Configuration du kernel Jupyter WSL...")
# Creer le dossier du kernel
self.kernel_path.mkdir(parents=True, exist_ok=True)
# Obtenir le nom d'utilisateur WSL
success, wsl_user, _ = self._run_wsl_command("whoami")
if not success or not wsl_user:
wsl_user = "root"
# Creer le wrapper script
if not self.create_wrapper_script(wsl_user):
return False
wrapper_expanded = self.wrapper_path.replace("~", f"/home/{wsl_user}")
# Creer kernel.json avec le wrapper bash
kernel_json = {
"argv": [
"wsl.exe", "-d", "Ubuntu", "--",
"bash", wrapper_expanded,
"-f", "{connection_file}"
],
"display_name": "Python (GameTheory WSL + OpenSpiel)",
"language": "python"
}
kernel_file = self.kernel_path / "kernel.json"
with open(kernel_file, 'w') as f:
json.dump(kernel_json, f, indent=2)
print(f"[+] Kernel cree: {kernel_file}")
return True
def run_diagnostic(self):
"""Execute le diagnostic complet et configure WSL si necessaire"""
print("=" * 60)
print(" CONFIGURATION WSL POUR OPENSPIEL")
print("=" * 60)
if not self.is_windows:
print("\n[OK] Vous n'etes pas sur Windows.")
print(" OpenSpiel peut etre installe directement: pip install open_spiel")
return
# 1. Verifier WSL
print("\n[1/4] Verification de WSL...")
wsl_status = self.check_wsl_available()
if wsl_status.get("available"):
print(" [OK] WSL disponible")
else:
print(f" [!] {wsl_status.get('reason', 'Non disponible')}")
print("\n Pour installer WSL, executez en PowerShell Admin:")
print(" wsl --install -d Ubuntu")
return
# 2. Verifier OpenSpiel dans WSL
print("\n[2/4] Verification d'OpenSpiel dans WSL...")
openspiel_status = self.check_wsl_openspiel()
if openspiel_status.get("installed"):
print(f" [OK] OpenSpiel installe: {openspiel_status['games_count']} jeux")
else:
print(f" [~] {openspiel_status.get('reason', 'Non installe')}")
print("\n[3/4] Installation complete (OpenSpiel + PySAT + scientifiques)...")
if not self.install_openspiel_wsl():
print(" [!] Echec de l'installation")
print(" Voir install_wsl_kernel.md pour l'installation manuelle")
return
# 4. Configurer le kernel
print("\n[4/4] Configuration du kernel Jupyter...")
if self.install_kernel():
print("\n" + "=" * 60)
print("[OK] Configuration WSL terminee !")
print("\n Pour utiliser OpenSpiel:")
print(" 1. Redemarrez VSCode completement (pas Reload Window)")
print(" 2. Selectionnez le kernel 'Python (GameTheory WSL + OpenSpiel)'")
print(" 3. Si le kernel echoue, verifiez le log:")
print(" wsl -d Ubuntu -- cat /tmp/kernel-wrapper.log")
print("=" * 60)
else:
print("\n[!] Echec de la configuration du kernel")
# Executer la configuration WSL si on est sur Windows et OpenSpiel n'est pas disponible
if IS_WINDOWS and not OPENSPIEL_OK:
wsl_manager = WSLOpenSpielManager()
wsl_manager.run_diagnostic()
elif IS_WINDOWS and OPENSPIEL_OK:
print("[OK] OpenSpiel deja disponible sur ce systeme.")
else:
print("[OK] Plateforme Linux/Mac - OpenSpiel installe directement.")