Skip to content

Commit 01ef850

Browse files
author
PyCompiler ARK++
committed
Adds rich plugin context APIs and improves BCASL/SDK utilities
Adds a comprehensive set of plugin SDK and BCASL enhancements to provide plugins richer, safer and more useful workspace/context primitives. Why: - Plugins require convenient, robust helpers to inspect workspaces, iterate files, detect venvs/managers, parse dependency files, analyze code, and generate reports; providing these improves plugin reliability and reduces duplicated logic across plugins. - PreCompileContext and BCASL integration benefit from explicit workspace metadata, safer iteration, caching control and exclusion handling to make pre-compile actions more predictable. - UI/dialog and engine helper code is hardened for thread-safety and platform differences, and many small formatting/cleanup tweaks reduce churn and improve readability. Key effects: - Extends the plugin context with structured dataclasses and many utility functions for workspace inspection, file-pattern matching, dependency parsing, venv detection, git/CI/docker introspection, code metrics, security scanning, command execution, caching, report generation, and template generation. - Enhances PreCompileContext with workspace metadata accessors, improved iter_files (deduplication, optional caching, exclusion handling), and convenience getters for patterns/required files; exposes is_workspace_valid and other helpers for safer plugin behavior. - Integrates workspace metadata into BCASL loader/runner so pre-compile runs receive consistent context. - Improves thread-safe dialog invocation and progress dialog behavior for various platforms/display servers. - Minor refactors and whitespace/formatting cleanup across many modules to standardize style and small logic/IO calls. Overall this makes plugin development and BCASL pre-compile processing more powerful and resilient, enabling more sophisticated plugins with less boilerplate.
1 parent c477653 commit 01ef850

49 files changed

Lines changed: 4081 additions & 764 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Core/Auto_Command_Builder/auto_build.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
# Copyright (C) 2025
32

43
"""

Core/Compiler/compiler.py

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -33,36 +33,36 @@ def _continue_compile_all(self):
3333
auto_detect_entry_points = ark_config.get("auto_detect_entry_points", True)
3434
compile_only_main_ark = ark_config.get("compile_only_main", False)
3535
main_file_names_ark = ark_config.get("main_file_names", ["main.py", "app.py"])
36-
36+
3737
# Déplacé depuis compile_all pour poursuivre après BCASL sans bloquer l'UI
3838
# Compteurs pour les exclusions
3939
exclusion_counts = {
4040
"site_packages": 0,
4141
"ark_patterns": 0,
4242
"no_entry_point": 0,
4343
"read_error": 0,
44-
"not_exists": 0
44+
"not_exists": 0,
4545
}
46-
46+
4747
def is_executable_script(path):
4848
# Vérifie que le fichier existe, n'est pas dans site-packages, et contient un point d'entrée
4949
if not os.path.exists(path):
5050
exclusion_counts["not_exists"] += 1
5151
return False
52-
52+
5353
# Vérifier les patterns d'exclusion depuis ARK_Main_Config.yml
5454
if should_exclude_file(path, self.workspace_dir, exclusion_patterns):
5555
exclusion_counts["ark_patterns"] += 1
5656
return False
57-
57+
5858
if "site-packages" in path:
5959
exclusion_counts["site_packages"] += 1
6060
return False
61-
61+
6262
# Si auto_detect_entry_points est désactivé, accepter tous les fichiers
6363
if not auto_detect_entry_points:
6464
return True
65-
65+
6666
try:
6767
with open(path, encoding="utf-8") as f:
6868
content = f.read()
@@ -88,8 +88,12 @@ def is_executable_script(path):
8888
use_nuitka = True
8989

9090
# L'option UI a priorité sur la config ARK
91-
compile_only_main = self.opt_main_only.isChecked() if hasattr(self, "opt_main_only") else compile_only_main_ark
92-
91+
compile_only_main = (
92+
self.opt_main_only.isChecked()
93+
if hasattr(self, "opt_main_only")
94+
else compile_only_main_ark
95+
)
96+
9397
# Sélection des fichiers à compiler selon le compilateur
9498
if use_nuitka:
9599
# Nuitka : compile tous les fichiers sélectionnés ou tous les fichiers du workspace
@@ -144,17 +148,27 @@ def is_executable_script(path):
144148
self.current_compiling.clear()
145149
self.processes.clear()
146150
self.progress.setRange(0, 0) # Mode indéterminé pendant toute la compilation
147-
151+
148152
# Afficher les informations de configuration ARK
149153
if ark_config:
150154
self.log.append("📋 Configuration ARK chargée depuis ARK_Main_Config.yml\n")
151155
# Afficher les paramètres de compilation utilisés
152-
self.log.append(f" • Patterns d'inclusion : {', '.join(inclusion_patterns)}\n")
153-
self.log.append(f" • Patterns d'exclusion : {len(exclusion_patterns)} pattern(s)\n")
154-
self.log.append(f" • Détection point d'entrée : {'Activée' if auto_detect_entry_points else 'Désactivée'}\n")
155-
self.log.append(f" • Compiler uniquement main : {'Oui' if compile_only_main else 'Non'}\n")
156-
157-
self.log.append(f"🔨 Compilation parallèle démarrée ({len(files_ok)} fichier(s))...\n")
156+
self.log.append(
157+
f" • Patterns d'inclusion : {', '.join(inclusion_patterns)}\n"
158+
)
159+
self.log.append(
160+
f" • Patterns d'exclusion : {len(exclusion_patterns)} pattern(s)\n"
161+
)
162+
self.log.append(
163+
f" • Détection point d'entrée : {'Activée' if auto_detect_entry_points else 'Désactivée'}\n"
164+
)
165+
self.log.append(
166+
f" • Compiler uniquement main : {'Oui' if compile_only_main else 'Non'}\n"
167+
)
168+
169+
self.log.append(
170+
f"🔨 Compilation parallèle démarrée ({len(files_ok)} fichier(s))...\n"
171+
)
158172

159173
self.set_controls_enabled(False)
160174
self.try_start_processes()

Core/Compiler/mainprocess.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@
1414
# limitations under the License.
1515

1616

17-
18-
1917
import json
2018
import os
2119
import platform
@@ -44,7 +42,6 @@
4442
# ACASL support removed (obsolete)
4543

4644

47-
4845
def try_start_processes(self):
4946
from PySide6.QtWidgets import QApplication
5047

@@ -59,12 +56,13 @@ def try_start_processes(self):
5956
self.progress.setRange(0, 1)
6057
self.progress.setValue(1)
6158
from PySide6.QtWidgets import QApplication
59+
6260
QApplication.processEvents()
6361
self.log.append("✔️ Toutes les compilations sont terminées.\n")
6462
# Exécuter immédiatement les hooks de succès des moteurs et restaurer l'UI
6563
try:
6664
hooks = getattr(self, "_pending_engine_success_hooks", [])
67-
for (eng, fpath) in hooks:
65+
for eng, fpath in hooks:
6866
try:
6967
eng.on_success(self, fpath)
7068
except Exception:
@@ -180,7 +178,7 @@ def start_compilation_process(self, file):
180178
process._cancel_file = cancel_file
181179
process.readyReadStandardOutput.connect(lambda p=process: self.handle_stdout(p))
182180
process.readyReadStandardError.connect(lambda p=process: self.handle_stderr(p))
183-
181+
184182
# Capture stderr data before process deletion to avoid accessing deleted C++ object
185183
def _on_finished(ec, es, p=process):
186184
try:
@@ -198,7 +196,7 @@ def _on_finished(ec, es, p=process):
198196
p.deleteLater()
199197
except Exception:
200198
pass
201-
199+
202200
process.finished.connect(_on_finished)
203201
self.processes.append(process)
204202
self.current_compiling.add(file)

Core/Compiler/process_killer.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
# See the License for the specific language governing permissions and
1414
# limitations under the License.
1515

16+
1617
def _kill_process_tree(pid: int, *, timeout: float = 5.0, log=None) -> bool:
1718
def _log(msg: str):
1819
try:

Core/MainWindow.py

Lines changed: 33 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@
1414
# limitations under the License.
1515

1616

17-
18-
1917
import asyncio
2018
import json
2119
import os
@@ -184,8 +182,7 @@ def __init__(self):
184182

185183
self.load_preferences()
186184
self.init_ui()
187-
188-
185+
189186
# Détection langue système si préférence = "System"
190187
import locale
191188

@@ -392,13 +389,13 @@ def dropEvent(self, event: QDropEvent):
392389

393390
def add_py_files_from_folder(self, folder):
394391
from Core.ark_config_loader import load_ark_config, should_exclude_file
395-
392+
396393
count = 0
397394
excluded_count = 0
398395
# Charger la configuration ARK pour les patterns d'exclusion
399396
ark_config = load_ark_config(self.workspace_dir)
400397
exclusion_patterns = ark_config.get("exclusion_patterns", [])
401-
398+
402399
for root, _, files in os.walk(folder):
403400
for f in files:
404401
if f.endswith(".py"):
@@ -409,12 +406,14 @@ def add_py_files_from_folder(self, folder):
409406
== self.workspace_dir
410407
):
411408
continue
412-
409+
413410
# Vérifier les patterns d'exclusion depuis ARK_Main_Config.yml
414-
if should_exclude_file(full_path, self.workspace_dir, exclusion_patterns):
411+
if should_exclude_file(
412+
full_path, self.workspace_dir, exclusion_patterns
413+
):
415414
excluded_count += 1
416415
continue
417-
416+
418417
if full_path not in self.python_files:
419418
self.python_files.append(full_path)
420419
relative_path = (
@@ -424,14 +423,14 @@ def add_py_files_from_folder(self, folder):
424423
)
425424
self.file_list.addItem(relative_path)
426425
count += 1
427-
426+
428427
# Afficher un message récapitulatif si des fichiers ont été exclus
429428
if excluded_count > 0:
430429
self.log_i18n(
431430
f"⏩ Exclusion appliquée : {excluded_count} fichier(s) exclu(s) selon ARK_Main_Config.yml",
432-
f"⏩ Exclusion applied: {excluded_count} file(s) excluded according to ARK_Main_Config.yml"
431+
f"⏩ Exclusion applied: {excluded_count} file(s) excluded according to ARK_Main_Config.yml",
433432
)
434-
433+
435434
return count
436435

437436
def select_workspace(self):
@@ -452,7 +451,7 @@ def apply_workspace_selection(self, folder: str, source: str = "ui") -> bool:
452451
QApplication.processEvents()
453452
except Exception:
454453
loading_dialog = None
455-
454+
456455
# Ensure target folder exists; auto-create if missing; never refuse
457456
if not folder:
458457
try:
@@ -553,10 +552,11 @@ def apply_workspace_selection(self, folder: str, source: str = "ui") -> bool:
553552
self.log_i18n("Dossier venv détecté.", "Venv folder detected.")
554553
except Exception:
555554
pass
556-
555+
557556
# Créer le fichier ARK_Main_Config.yml s'il n'existe pas
558557
try:
559558
from Core.ark_config_loader import create_default_ark_config
559+
560560
if create_default_ark_config(folder):
561561
self.log_i18n(
562562
"📋 Fichier ARK_Main_Config.yml créé dans le workspace.",
@@ -567,14 +567,14 @@ def apply_workspace_selection(self, folder: str, source: str = "ui") -> bool:
567567
f"⚠️ Impossible de créer ARK_Main_Config.yml: {e}",
568568
f"⚠️ Failed to create ARK_Main_Config.yml: {e}",
569569
)
570-
570+
571571
# Fermer le dialog de chargement
572572
try:
573573
if loading_dialog:
574574
loading_dialog.close()
575575
except Exception:
576576
pass
577-
577+
578578
return True
579579
except Exception as _e:
580580
try:
@@ -943,13 +943,14 @@ def open_ark_config(self):
943943
),
944944
)
945945
return
946-
946+
947947
config_path = os.path.join(self.workspace_dir, "ARK_Main_Config.yml")
948-
948+
949949
# Créer le fichier s'il n'existe pas
950950
if not os.path.exists(config_path):
951951
try:
952952
from Core.ark_config_loader import create_default_ark_config
953+
953954
if create_default_ark_config(self.workspace_dir):
954955
self.log_i18n(
955956
"📋 Fichier ARK_Main_Config.yml créé.",
@@ -965,20 +966,20 @@ def open_ark_config(self):
965966
),
966967
)
967968
return
968-
969+
969970
# Ouvrir le fichier dans l'éditeur par défaut
970971
try:
971972
import subprocess
972973
import platform
973-
974+
974975
system = platform.system()
975976
if system == "Windows":
976977
os.startfile(config_path)
977978
elif system == "Darwin": # macOS
978979
subprocess.run(["open", config_path])
979980
else: # Linux
980981
subprocess.run(["xdg-open", config_path])
981-
982+
982983
self.log_i18n(
983984
f"📝 Ouverture de {config_path}",
984985
f"📝 Opening {config_path}",
@@ -1579,6 +1580,7 @@ def _on_result(res):
15791580
# Registry-based propagation to engine instances
15801581
try:
15811582
import Core.engines_loader as engines_loader
1583+
15821584
engines_loader.registry.apply_translations(self, tr)
15831585
except Exception:
15841586
pass
@@ -1741,7 +1743,6 @@ def _safe_log(self, text):
17411743
except Exception:
17421744
pass
17431745

1744-
17451746
def _has_active_background_tasks(self):
17461747
# Compilation en cours
17471748
if self.processes:
@@ -1777,9 +1778,9 @@ def closeEvent(self, event):
17771778
details.append("compilation")
17781779
if hasattr(self, "venv_manager") and self.venv_manager:
17791780
details.extend(self.venv_manager.get_active_task_labels("Français"))
1780-
1781+
17811782
is_english = getattr(self, "current_language", "Français") == "English"
1782-
1783+
17831784
if is_english:
17841785
mapping = {
17851786
"compilation": "build",
@@ -1788,7 +1789,7 @@ def closeEvent(self, event):
17881789
"vérification/installation du venv": "venv check/installation",
17891790
}
17901791
details_disp = [mapping.get(d, d) for d in details]
1791-
1792+
17921793
# Construire le message détaillé
17931794
title = "⚠️ Process Running"
17941795
msg = "A process is currently running:\n\n"
@@ -1798,12 +1799,12 @@ def closeEvent(self, event):
17981799
msg += "\n"
17991800
msg += "If you quit now, the process will be stopped and any unsaved work will be lost.\n\n"
18001801
msg += "Do you really want to quit?"
1801-
1802+
18021803
yes_text = "Yes, Quit"
18031804
no_text = "No, Continue"
18041805
else:
18051806
details_disp = details
1806-
1807+
18071808
# Construire le message détaillé
18081809
title = "⚠️ Processus en cours"
18091810
msg = "Un processus est actuellement en cours :\n\n"
@@ -1813,24 +1814,24 @@ def closeEvent(self, event):
18131814
msg += "\n"
18141815
msg += "Si vous quittez maintenant, le processus sera arrêté et tout travail non sauvegardé sera perdu.\n\n"
18151816
msg += "Voulez-vous vraiment quitter ?"
1816-
1817+
18171818
yes_text = "Oui, Quitter"
18181819
no_text = "Non, Continuer"
1819-
1820+
18201821
# Créer la boîte de dialogue avec des boutons personnalisés
18211822
msgbox = QMessageBox(self)
18221823
msgbox.setWindowTitle(title)
18231824
msgbox.setText(msg)
18241825
msgbox.setIcon(QMessageBox.Warning)
18251826
msgbox.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
18261827
msgbox.setDefaultButton(QMessageBox.No)
1827-
1828+
18281829
# Personnaliser les textes des boutons
18291830
msgbox.button(QMessageBox.Yes).setText(yes_text)
18301831
msgbox.button(QMessageBox.No).setText(no_text)
1831-
1832+
18321833
reply = msgbox.exec()
1833-
1834+
18341835
if reply == QMessageBox.Yes:
18351836
self._closing = True
18361837
# Annule les compilations en cours si nécessaire

0 commit comments

Comments
 (0)