Skip to content

Commit 4f3ac92

Browse files
committed
Refactor: Séparation des exclusions workspace/build et simplification de BCASL (retrait timeout/parallélisme)
1 parent 7a092f3 commit 4f3ac92

15 files changed

Lines changed: 73 additions & 434 deletions

File tree

Core/AdvancedConfigEditor.py

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,12 @@ def validate_payload(file_id: str, data: Any) -> tuple[list[str], list[str]]:
374374
errs.append("build.entrypoint must be a string or null.")
375375
if isinstance(ep, str) and not ep.strip():
376376
warns.append("build.entrypoint is empty.")
377+
exclude = build.get("exclude")
378+
if exclude is not None and (
379+
not isinstance(exclude, list)
380+
or not all(isinstance(item, str) for item in exclude)
381+
):
382+
errs.append("build.exclude must be a list of strings.")
377383

378384
deps = data.get("dependencies")
379385
if deps is not None and not isinstance(deps, dict):
@@ -428,14 +434,6 @@ def validate_payload(file_id: str, data: Any) -> tuple[list[str], list[str]]:
428434
if isinstance(options, dict):
429435
if "enabled" in options and not isinstance(options.get("enabled"), bool):
430436
errs.append("options.enabled must be a boolean.")
431-
if "plugin_timeout_s" in options and not isinstance(
432-
options.get("plugin_timeout_s"), (int, float)
433-
):
434-
errs.append("options.plugin_timeout_s must be numeric.")
435-
if "plugin_parallelism" in options and not isinstance(
436-
options.get("plugin_parallelism"), int
437-
):
438-
errs.append("options.plugin_parallelism must be an integer.")
439437

440438
plugins = data.get("plugins")
441439
if plugins is not None and not isinstance(plugins, dict):
@@ -498,9 +496,7 @@ def make_default_content(
498496
"exclude_patterns": ["**/__pycache__/**", ".venv/**", "venv/**"],
499497
"options": {
500498
"enabled": True,
501-
"plugin_timeout_s": 0.0,
502499
"sandbox": True,
503-
"plugin_parallelism": 0,
504500
"iter_files_cache": True,
505501
},
506502
"plugins": {},

Core/Configs/__init__.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@
104104
"engine": "pyinstaller",
105105
"output": "dist/",
106106
"data": [],
107+
"exclude": [],
107108
},
108109
}
109110

@@ -178,6 +179,10 @@ def _normalize_list(values: Any) -> list[Any]:
178179
return [item for item in values if item is not None and str(item).strip()]
179180

180181

182+
def _normalize_build_exclude(values: Any) -> list[str]:
183+
return list(dict.fromkeys(pattern for pattern in _normalize_list(values) if pattern))
184+
185+
181186
def _normalize_workspace_exclude(values: Any) -> list[str]:
182187
merged: list[str] = []
183188
for item in DEFAULT_EXCLUDE_PATTERNS:
@@ -291,6 +296,7 @@ def normalize_ark_config(config: dict[str, Any]) -> dict[str, Any]:
291296
for item in _normalize_list(build.get("data"))
292297
if isinstance(item, dict)
293298
],
299+
"exclude": _normalize_build_exclude(build.get("exclude")),
294300
}
295301
icon_value = build.get("icon")
296302
if isinstance(icon_value, str) and icon_value.strip():
@@ -369,6 +375,10 @@ def validate_ark_config(
369375
if not isinstance(exclude_patterns, list):
370376
errors.append("workspace.exclude must be a list")
371377

378+
build_exclude_patterns = build.get("exclude")
379+
if not isinstance(build_exclude_patterns, list):
380+
errors.append("build.exclude must be a list")
381+
372382
return ArkConfigValidationResult(
373383
config=normalized, warnings=warnings, errors=errors
374384
)

Core/Locking/__init__.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,8 @@ def build_lock_payload(
133133
dependencies: dict[str, str] | None = None,
134134
) -> dict[str, Any]:
135135
config = normalize_ark_config(config)
136-
exclude_patterns = list(((config.get("workspace") or {}).get("exclude")) or [])
137136
build = config.get("build") or {}
137+
exclude_patterns = list(build.get("exclude") or [])
138138
project = config.get("project") or {}
139139
ensure_workspace_layout(workspace)
140140
lock_dir = workspace / ".ark" / "lock"
@@ -150,6 +150,7 @@ def build_lock_payload(
150150
"build": {
151151
"output": build.get("output"),
152152
"data": list(build.get("data") or []),
153+
"exclude": exclude_patterns,
153154
**({"icon": build.get("icon")} if build.get("icon") else {}),
154155
},
155156
"engine": {
@@ -207,12 +208,11 @@ def build_context_from_ark_config(config: dict[str, Any]) -> BuildContext:
207208
config = normalize_ark_config(config)
208209
project = config.get("project") or {}
209210
build = config.get("build") or {}
210-
workspace_cfg = config.get("workspace") or {}
211211
return BuildContext(
212212
project_name=str(project.get("name") or ""),
213213
entry_point=str(project.get("entry") or ""),
214214
output_dir=str(build.get("output") or ""),
215-
exclude_patterns=list(workspace_cfg.get("exclude") or []),
215+
exclude_patterns=list(build.get("exclude") or []),
216216
data_mappings=list(build.get("data") or []),
217217
icon=str(build.get("icon")) if build.get("icon") else None,
218218
)
@@ -222,11 +222,17 @@ def build_context_from_lock(lock_payload: dict[str, Any]) -> BuildContext:
222222
project = lock_payload.get("project") or {}
223223
build = lock_payload.get("build") or {}
224224
workspace_cfg = lock_payload.get("workspace") or {}
225+
226+
# Check build.exclude (new) then workspace.exclude_patterns (legacy)
227+
exclude_patterns = list(build.get("exclude") or [])
228+
if not exclude_patterns:
229+
exclude_patterns = list(workspace_cfg.get("exclude_patterns") or [])
230+
225231
return BuildContext(
226232
project_name=str(project.get("name") or ""),
227233
entry_point=str(project.get("entry") or ""),
228234
output_dir=str(build.get("output") or ""),
229-
exclude_patterns=list(workspace_cfg.get("exclude_patterns") or []),
235+
exclude_patterns=exclude_patterns,
230236
data_mappings=list(build.get("data") or []),
231237
icon=str(build.get("icon")) if build.get("icon") else None,
232238
)

README.md

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ Build Python apps with a predictable workflow, a configurable pre-compile pipeli
1414

1515
## Core capabilities
1616

17-
- **BCASL pre-compile pipeline**: validation, preparation, transformation before the build, with timeouts and safety controls.
17+
- **BCASL pre-compile pipeline**: validation, preparation, transformation before the build, with safety controls.
1818
- **Unified EngineRunner architecture**: a single source of truth for both CLI and GUI compilation, ensuring identical build results across all interfaces.
1919
- **BuildContext-driven builds**: engines receive a normalized project context, abstracting away the source of configuration (YAML vs. Lock files).
2020
- **Multi-engine support**: switch between PyInstaller, Nuitka, and cx_Freeze seamlessly.
@@ -67,8 +67,7 @@ Workspace
6767
|-- Load bcasl.yml
6868
|-- Discover plugins (Plugins/)
6969
|-- Enable / order / priorities
70-
|-- Sandboxed execution (timeouts,
71-
| optional parallelism)
70+
|-- Sandboxed execution (optional)
7271
|
7372
v
7473
Compilation (PyInstaller / Nuitka / cx_Freeze)
@@ -93,8 +92,8 @@ python3 pycompiler_ark.py build --lock latest.lock # Rebuild from lock file
9392

9493
# Execution
9594
python3 pycompiler_ark.py run bcasl # Execute BCASL pipeline
96-
python3 pycompiler_ark.py run bcasl --timeout 30 # With custom timeout
97-
95+
python3 pycompiler_ark.py run bcasl --list-plugins # List active plugins
96+
```
9897
# GUI
9998
python3 pycompiler_ark.py gui # Launch modern IDE-like GUI
10099
python3 pycompiler_ark.py gui --legacy # Launch classic GUI
@@ -135,7 +134,7 @@ python3 pycompiler_ark.py init --entry main.py --json
135134

136135
## Configuration
137136

138-
- **`ark.yml`**: inclusion/exclusion patterns, build entrypoint, and a few workspace-level defaults consumed by BCASL bootstrap.
137+
- **`ark.yml`**: workspace/build exclusion patterns, build entrypoint, and a few workspace-level defaults consumed by BCASL bootstrap.
139138
- **`bcasl.yml`**: plugin enable/disable, order, and timeouts.
140139

141140
---

Ui/Cli/app.py

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -308,20 +308,15 @@ def run_group():
308308
"""Run developer-facing commands."""
309309

310310
@run_group.command("bcasl")
311-
@click.option("--timeout", type=float)
312-
@click.option("--parallel", type=int)
313311
@click.option("--list-plugins", is_flag=True)
314312
@click.option("--verbose", "-v", is_flag=True)
315-
def run_bcasl_cmd(timeout, parallel, list_plugins, verbose):
313+
def run_bcasl_cmd(list_plugins, verbose):
316314
"""Run BCASL in headless mode for the current workspace."""
317315
args: list[str]
318316
if list_plugins:
319317
args = ["list"]
320318
else:
321319
args = ["run", str(Path.cwd())]
322-
if timeout is not None:
323-
args.extend(["--timeout", str(timeout)])
324-
del parallel
325320
raise click.exceptions.Exit(run_bcasl_headless(args, verbose=verbose))
326321

327322
@cli.command("gui")

Ui/Gui/Compilation/mainprocess.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -460,10 +460,10 @@ def get_exclusion_patterns(self) -> List[str]:
460460
if self._workspace_dir:
461461
try:
462462
config = load_ark_config(self._workspace_dir)
463-
# On utilise la nouvelle structure workspace: exclude
464-
workspace_cfg = config.get("workspace", {})
465-
if isinstance(workspace_cfg, dict):
466-
return workspace_cfg.get("exclude", DEFAULT_EXCLUDE_PATTERNS)
463+
# Pour la compilation, on utilise désormais build: exclude
464+
build_cfg = config.get("build", {})
465+
if isinstance(build_cfg, dict):
466+
return build_cfg.get("exclude", DEFAULT_EXCLUDE_PATTERNS)
467467
except Exception:
468468
pass
469469
return DEFAULT_EXCLUDE_PATTERNS

bcasl/Loader.py

Lines changed: 13 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ def _discover_bcasl_meta(Plugins_dir: Path) -> dict[str, dict[str, Any]]:
8888
spec.loader.exec_module(module) # type: ignore[attr-defined]
8989

9090
# Utilise un gestionnaire temporaire pour enregistrer et lire les métadonnées
91-
mgr = BCASL(Plugins_dir, config={}, sandbox=False, plugin_timeout_s=0.0) # type: ignore[call-arg]
91+
mgr = BCASL(Plugins_dir, config={}, sandbox=False) # type: ignore[call-arg]
9292

9393
# Méthode 1: chercher fonction bcasl_register
9494
reg = getattr(module, "bcasl_register", None)
@@ -185,7 +185,7 @@ def _discover_bcasl_plugins(
185185
"""Load les plugins et return un mapping plugin_id -> instance."""
186186
plugins: dict[str, BcPluginBase] = {}
187187
try:
188-
mgr = BCASL(workspace_root, config=cfg, sandbox=False, plugin_timeout_s=0.0)
188+
mgr = BCASL(workspace_root, config=cfg, sandbox=False)
189189
mgr.load_plugins_from_directory(Plugins_dir)
190190
for pid, rec in getattr(mgr, "_registry", {}).items():
191191
try:
@@ -213,23 +213,6 @@ def _emit_log(log_cb: Optional[callable], message: str) -> None:
213213
pass
214214

215215

216-
def _resolve_plugin_timeout(cfg: dict[str, Any]) -> float:
217-
"""Résout le timeout effectif (config > env), <= 0 => illimité."""
218-
try:
219-
env_timeout = float(os.environ.get("PYCOMPILER_BCASL_PLUGIN_TIMEOUT", "0"))
220-
except Exception:
221-
env_timeout = 0.0
222-
try:
223-
opt = cfg.get("options", {}) if isinstance(cfg, dict) else {}
224-
cfg_timeout = (
225-
float(opt.get("plugin_timeout_s", 0.0)) if isinstance(opt, dict) else 0.0
226-
)
227-
except Exception:
228-
cfg_timeout = 0.0
229-
plugin_timeout = cfg_timeout if cfg_timeout != 0.0 else env_timeout
230-
return float(plugin_timeout) if plugin_timeout and plugin_timeout > 0 else 0.0
231-
232-
233216
def _is_bcasl_enabled(workspace_root: Path) -> bool:
234217
"""Consulte ark.yml pour savoir si le BCASL est activé globalement."""
235218
try:
@@ -306,7 +289,6 @@ def _run_bcasl_sync(
306289
workspace_root: Path,
307290
plugins_dir: Path,
308291
cfg: dict[str, Any],
309-
plugin_timeout: float,
310292
log_cb: Optional[callable] = None,
311293
stop_requested: Optional[callable] = None,
312294
build_context: Optional[Any] = None,
@@ -315,7 +297,6 @@ def _run_bcasl_sync(
315297
manager = BCASL(
316298
workspace_root,
317299
config=cfg,
318-
plugin_timeout_s=plugin_timeout,
319300
build_context=build_context,
320301
)
321302
loaded, errors = manager.load_plugins_from_directory(plugins_dir)
@@ -439,10 +420,15 @@ def _read_yml(p: Path) -> dict[str, Any]:
439420

440421
ark_config = load_ark_config(str(workspace_root))
441422

442-
# exclusion_patterns est désormais dans workspace: exclude
443-
workspace_cfg = ark_config.get("workspace", {})
444-
if isinstance(workspace_cfg, dict):
445-
exclude_patterns = workspace_cfg.get("exclude", exclude_patterns)
423+
# On privilégie build: exclude pour les actions pré-compilation
424+
build_cfg = ark_config.get("build", {})
425+
if isinstance(build_cfg, dict) and "exclude" in build_cfg:
426+
exclude_patterns = build_cfg.get("exclude", exclude_patterns)
427+
else:
428+
# Fallback legacy ou si build: exclude est absent (peu probable avec normalization)
429+
workspace_cfg = ark_config.get("workspace", {})
430+
if isinstance(workspace_cfg, dict):
431+
exclude_patterns = workspace_cfg.get("exclude", exclude_patterns)
446432

447433
# inclusion_patterns n'est plus supporté (l'exclusion suffit)
448434

@@ -467,9 +453,7 @@ def _read_yml(p: Path) -> dict[str, Any]:
467453
"file_patterns": file_patterns,
468454
"exclude_patterns": exclude_patterns,
469455
"options": {
470-
"plugin_timeout_s": plugin_timeout,
471456
"sandbox": True,
472-
"plugin_parallelism": 0,
473457
"iter_files_cache": True,
474458
},
475459
"phases": default_phases,
@@ -547,17 +531,8 @@ def ensure_bcasl_thread_stopped(self, timeout_ms: int = 5000) -> None:
547531

548532

549533
def resolve_bcasl_timeout(self) -> float:
550-
"""Résout le timeout effectif des plugins à partir de la config et de l'env.
551-
<= 0 => illimité (0.0 renvoyé)
552-
"""
553-
try:
554-
if not getattr(self, "workspace_dir", None):
555-
return 0.0
556-
workspace_root = Path(self.workspace_dir).resolve()
557-
cfg = _load_workspace_config(workspace_root)
558-
return _resolve_plugin_timeout(cfg)
559-
except Exception:
560-
return 0.0
534+
"""Legacy resolve_bcasl_timeout - returns 0.0 (no timeout)."""
535+
return 0.0
561536

562537

563538
def open_bc_loader_dialog(self) -> None:
@@ -647,7 +622,6 @@ def run_pre_compile_async(
647622
Plugins_dir = _get_plugins_dir()
648623

649624
cfg = _load_workspace_config(workspace_root)
650-
plugin_timeout = _resolve_plugin_timeout(cfg)
651625

652626
# Vérifier si BCASL est activé globalement via ark.yml
653627
if not _is_bcasl_enabled(workspace_root):
@@ -676,7 +650,6 @@ def run_pre_compile_async(
676650
workspace_root,
677651
Plugins_dir,
678652
cfg,
679-
plugin_timeout,
680653
build_context=build_context,
681654
)
682655
try:
@@ -709,7 +682,6 @@ def run_pre_compile_async(
709682
workspace_root,
710683
Plugins_dir,
711684
cfg,
712-
plugin_timeout,
713685
log_cb=log_cb,
714686
build_context=build_context,
715687
)
@@ -747,7 +719,6 @@ def run_pre_compile(self, build_context: Optional[Any] = None) -> Optional[objec
747719
Plugins_dir = _get_plugins_dir()
748720

749721
cfg = _load_workspace_config(workspace_root)
750-
plugin_timeout = _resolve_plugin_timeout(cfg)
751722

752723
# Vérifier si BCASL est activé globalement via ark.yml
753724
if not _is_bcasl_enabled(workspace_root):
@@ -765,7 +736,6 @@ def run_pre_compile(self, build_context: Optional[Any] = None) -> Optional[objec
765736
workspace_root,
766737
Plugins_dir,
767738
cfg,
768-
plugin_timeout,
769739
log_cb=log_cb,
770740
build_context=build_context,
771741
)

0 commit comments

Comments
 (0)