77import json
88import os
99import sys
10- from concurrent .futures import ProcessPoolExecutor , as_completed
1110from importlib .metadata import version
1211from pathlib import Path
1312
14- from vesskel ._batch import _SUPPORTED_EXTENSIONS , _write_csv , process_one
1513from vesskel .config import (
1614 ExtractionConfig ,
1715 OutputConfig ,
@@ -91,7 +89,9 @@ def _make_parser() -> argparse.ArgumentParser:
9189 return parser
9290
9391
94- # intercept shell completion requests early, before heavy imports
92+ # WARNING: keep this guard and everything above it free of heavy imports (numpy, PIL, vesskel.pipeline, vesskel._batch).
93+ # The guard lets shell completions exit in 81 ms instead of 800 ms.
94+ # Tests in tests/test_cli.py::TestCompletionSpeed enforce this.
9595if "_ARGCOMPLETE" in os .environ or (len (sys .argv ) > 1 and sys .argv [1 ] == "completions" ):
9696 if "_ARGCOMPLETE" in os .environ :
9797 try :
@@ -111,6 +111,11 @@ def _make_parser() -> argparse.ArgumentParser:
111111 sys .exit (0 )
112112
113113
114+ _SUPPORTED_EXTENSIONS = frozenset (
115+ {".png" , ".jpg" , ".jpeg" , ".tif" , ".tiff" , ".bmp" , ".npy" }
116+ )
117+
118+
114119def _parse_args () -> argparse .Namespace :
115120 parser = _make_parser ()
116121 try :
@@ -158,6 +163,8 @@ def _compute_safe_names(input_paths: list[Path]) -> list[str]:
158163
159164
160165def _run_batch (args : argparse .Namespace ) -> int :
166+ from vesskel ._batch import _write_csv , process_one
167+
161168 config = load_pipeline_config (Path (args .config ))
162169 input_paths = _discover_input_paths (args .input , recursive = args .recursive )
163170 if not input_paths :
@@ -201,6 +208,9 @@ def _run_parallel(
201208 total : int ,
202209) -> list [dict [str , object ]]:
203210 import multiprocessing as mp
211+ from concurrent .futures import ProcessPoolExecutor , as_completed
212+
213+ from vesskel ._batch import process_one
204214
205215 summary_rows : list [dict [str , object ]] = []
206216 ctx = mp .get_context ("spawn" )
0 commit comments