Files
pdf_oralia_vibe/src/plesna_gerance/paths.py
Bertrand Benjamin a90d1c4f64 feat: mode application de bureau + packaging Windows autonome
Permet de distribuer l'application en executable Windows double-clic,
sans Python/Node/terminal pour l'utilisateur final.

- `desktop.py` : demarre le serveur sur un port libre dans un thread et
  ouvre une fenetre native (pywebview). `PLESNA_DEBUG=1` active les
  DevTools. Commande `plesna-gerance desktop` (import paresseux).
- `paths.py` : resolution centralisee des chemins selon le contexte
  (dev, executable PyInstaller, surcharge par variables d'env). Les
  donnees vont dans un dossier utilisateur inscriptible (%APPDATA%),
  les ressources dans le bundle. connection.py/storage.py utilisent
  `get_data_dir()`, app.py `resource_path()`.
- app.py : type MIME `.mjs` force (modules ES / worker PDF.js sous
  Windows).
- Groupes de deps optionnels desktop / desktop-linux / build.
- packaging/ : spec PyInstaller, build_windows.ps1, installeur Inno
  Setup. Workflow GitHub build-windows.yml (runner Windows).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-19 17:21:27 +02:00

71 lines
2.4 KiB
Python

"""Résolution centralisée des chemins (données utilisateur et ressources).
Gère trois contextes d'exécution :
- **Développement** : lancé depuis les sources. Les données vivent dans
``<racine_projet>/data`` et les ressources (frontend buildé) dans
``<racine_projet>/frontend/dist``.
- **Exécutable empaqueté** (PyInstaller, ex. ``.exe`` Windows) : les données
doivent aller dans un dossier utilisateur inscriptible (``%APPDATA%`` sous
Windows) — l'exécutable lui-même est souvent en lecture seule
(``Program Files``). Les ressources sont extraites dans ``sys._MEIPASS``.
- **Surcharge explicite** via variables d'environnement (tests, Docker).
"""
import os
import sys
from pathlib import Path
APP_NAME = "PlesnaGerance"
def is_frozen() -> bool:
"""Vrai si on tourne depuis un exécutable PyInstaller."""
return getattr(sys, "frozen", False)
def get_bundle_dir() -> Path:
"""Répertoire racine des ressources embarquées (lecture seule).
- Empaqueté : ``sys._MEIPASS`` (dossier d'extraction PyInstaller), avec
repli sur le dossier de l'exécutable.
- Développement : racine du projet (``src/plesna_gerance/paths.py`` -> 3 crans).
"""
if is_frozen():
meipass = getattr(sys, "_MEIPASS", None)
if meipass:
return Path(meipass)
return Path(sys.executable).resolve().parent
return Path(__file__).resolve().parent.parent.parent
def resource_path(*parts: str) -> Path:
"""Chemin d'une ressource embarquée (ex. ``resource_path('frontend', 'dist')``)."""
return get_bundle_dir().joinpath(*parts)
def get_data_dir() -> Path:
"""Répertoire inscriptible des données utilisateur (DB, documents).
Priorité :
1. ``PLESNA_DATA_DIR`` si défini ;
2. dossier applicatif utilisateur si empaqueté ;
3. ``<racine_projet>/data`` en développement (comportement historique).
"""
env = os.environ.get("PLESNA_DATA_DIR")
if env:
return Path(env)
if is_frozen():
if sys.platform == "win32":
base = Path(os.environ.get("APPDATA") or Path.home())
elif sys.platform == "darwin":
base = Path.home() / "Library" / "Application Support"
else:
base = Path(
os.environ.get("XDG_DATA_HOME") or (Path.home() / ".local" / "share")
)
return base / APP_NAME
return get_bundle_dir() / "data"