Skip to content

Commit 423418b

Browse files
committed
feat: uv_ffi version enforcement + worker daemon hardening
- sync_interpreter now upgrades uv_ffi to pyproject.toml minimum on every interpreter sync, not just omnipkg itself - stale omnipkg dist-info dirs cleaned up after successful sync - worker pool warm-worker selection fixed (sort-then-pop vs broken temp-requeue) - worker task timeout lifted to OMNIPKG_WORKER_TIMEOUT (default 360s) from hardcoded 60s - execute_with_timeout default raised to 240s - pygments>=2.20.0 added (ReDoS CVE in AdlLexer) - uv_ffi minimum bumped to post4 Modified: • src/omnipkg/core.py (+66/-9 lines) • src/omnipkg/isolation/worker_daemon.py (+17/-20 lines) • pyproject.toml (+5/-2 lines) • requirements-trace.txt (+4/-2 lines) [gitship-generated]
1 parent 95a998e commit 423418b

4 files changed

Lines changed: 92 additions & 33 deletions

File tree

pyproject.toml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ backend-path = ["."]
66

77
[project]
88
name = "omnipkg"
9-
version = "2.5.1"
9+
version = "2.6.0"
1010
authors = [
1111
{ name = "1minds3t", email = "1minds3t@proton.me" },
1212
]
@@ -39,12 +39,15 @@ classifiers = [
3939
]
4040

4141
dependencies = [
42-
"uv_ffi>=0.10.8.post3 ; python_version >= '3.8' and python_version <= '3.14'",
42+
"uv_ffi>=0.10.8.post4 ; python_version >= '3.8' and python_version <= '3.14'",
4343
"requests>=2.20",
4444
"psutil>=5.9.0",
4545
"typer>=0.4.0",
4646
"rich>=10.0.0",
4747

48+
# Pygments - ReDoS CVE in AdlLexer, fixed in 2.20.0
49+
"pygments>=2.20.0",
50+
4851
# werkzeug - pinned to last available per Python, CVEs unresolved due to Python constraints
4952
# TODO: werkzeug-lts needed for py37/py38 to backport CVE-2026-27199, CVE-2026-21860, CVE-2025-66221
5053
"werkzeug>=3.0.6; python_version == '3.8'", # stuck - upstream dropped 3.8 before 3.1.x

requirements-trace.txt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,10 @@ py-serializable==2.1.0
106106
# via cyclonedx-python-lib
107107
pycparser==3.0
108108
# via cffi
109-
pygments==2.19.2
110-
# via rich
109+
pygments==2.20.0
110+
# via
111+
# omnipkg (pyproject.toml)
112+
# rich
111113
pyparsing==3.3.2
112114
# via pip-requirements-parser
113115
requests==2.33.1

src/omnipkg/core.py

Lines changed: 66 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6355,12 +6355,36 @@ def _self_heal_omnipkg_installation(self):
63556355
# === TIER 2: PATH-BASED SYNC CHECK (1-2ms) ===
63566356
sync_needed = self._check_sync_status_ultra_fast(master_version_str)
63576357

6358-
if not sync_needed:
6358+
# Always collect ALL managed interpreters for uv_ffi check,
6359+
# even if omnipkg version is already current on all of them.
6360+
all_managed = []
6361+
try:
6362+
interp_dir = self.config_manager.venv_path / ".omnipkg" / "interpreters"
6363+
if interp_dir.exists():
6364+
import glob as _iglob
6365+
bin_sub = "Scripts" if platform.system() == "Windows" else "bin"
6366+
for _idir in sorted(interp_dir.iterdir()):
6367+
_bin = _idir / bin_sub
6368+
if not _bin.exists():
6369+
continue
6370+
_py = next(
6371+
(p for p in sorted(_bin.iterdir())
6372+
if p.name.startswith("python3.") and p.suffix == "" and p.is_file()),
6373+
None
6374+
)
6375+
if _py:
6376+
import re as _re2
6377+
_m = _re2.search(r"python(\d+\.\d+)", _py.name)
6378+
if _m:
6379+
all_managed.append((_m.group(1), str(_py)))
6380+
except Exception:
6381+
pass
6382+
6383+
if not sync_needed and not all_managed:
63596384
# Write cache for next time
63606385
self._write_heal_cache(
63616386
{"master_version": master_version_str, "timestamp": time.time()}
63626387
)
6363-
63646388
if "--verbose" in sys.argv or "-V" in sys.argv:
63656389
elapsed_ns = time.perf_counter_ns() - overall_start
63666390
elapsed_ms = elapsed_ns / 1_000_000
@@ -6369,6 +6393,12 @@ def _self_heal_omnipkg_installation(self):
63696393
)
63706394
return
63716395

6396+
# Merge: sync_needed takes priority; add remaining from all_managed for uv_ffi-only check
6397+
sync_needed_exes = {exe for _, exe in sync_needed}
6398+
for ver, exe in all_managed:
6399+
if exe not in sync_needed_exes:
6400+
sync_needed.append((ver, exe))
6401+
63726402
# === TIER 3: HEALING REQUIRED (ONLY WHEN NECESSARY) ===
63736403
# Extra safety: Warn on Windows
63746404
if platform.system() == "Windows":
@@ -6667,10 +6697,15 @@ def _perform_concurrent_healing(
66676697
native_needs_sync = False
66686698
native_was_synced = False
66696699
_sync_marker = native_exe.parent / f".omnipkg_synced_{master_version}"
6670-
if _sync_marker.exists():
6671-
return # already synced this version, nothing to do
6672-
6673-
if is_current_native:
6700+
_native_already_synced = _sync_marker.exists()
6701+
if _native_already_synced and os.environ.get("OMNIPKG_DEBUG"):
6702+
safe_print(f" [HEAL] Native marker exists for v{master_version}, skipping native sync only")
6703+
6704+
if _native_already_synced:
6705+
native_needs_sync = False
6706+
if os.environ.get("OMNIPKG_DEBUG"):
6707+
safe_print(f" [HEAL] Native already at v{master_version}, skipping native sync")
6708+
elif is_current_native:
66746709
# We ARE the native interpreter - check if we need sync
66756710
try:
66766711
native_site_packages = site.getsitepackages()[0]
@@ -6763,8 +6798,11 @@ def _perform_concurrent_healing(
67636798

67646799
def sync_interpreter(py_ver, target_exe):
67656800
_marker = Path(target_exe).parent / f".omnipkg_synced_{master_version}"
6766-
if _marker.exists():
6767-
return (py_ver, True)
6801+
_already_synced = _marker.exists()
6802+
if _already_synced:
6803+
# Still need to check uv_ffi even if omnipkg is already synced
6804+
if os.environ.get("OMNIPKG_DEBUG"):
6805+
safe_print(f" [HEAL] {py_ver} omnipkg marker exists, checking uv_ffi only...")
67686806
# Detect egg-based pip (e.g. pip 20.2.2 on Python 3.7)
67696807
# and inject it into PYTHONPATH so -m pip works in subprocess
67706808
import glob, os
@@ -6811,6 +6849,25 @@ def sync_interpreter(py_ver, target_exe):
68116849
easy_pth.unlink()
68126850
except Exception:
68136851
pass
6852+
# Clean up stale omnipkg dist-info dirs (leave only current version)
6853+
if result.returncode == 0:
6854+
try:
6855+
sp_path = Path(target_exe).parent.parent / "lib"
6856+
import glob as _glob2
6857+
for sp_dir in _glob2.glob(str(sp_path / "python*/site-packages")):
6858+
sp_dir = Path(sp_dir)
6859+
for di in sp_dir.glob("omnipkg-*.dist-info"):
6860+
ver_in_name = di.name.replace("omnipkg-", "").replace(".dist-info", "")
6861+
if ver_in_name != master_version:
6862+
import shutil as _shutil
6863+
_shutil.rmtree(str(di), ignore_errors=True)
6864+
except Exception:
6865+
pass
6866+
# uv_ffi check runs whether we just synced OR were already synced
6867+
if _already_synced or result.returncode == 0:
6868+
pass # fall through to uv_ffi check below
6869+
else:
6870+
return (py_ver, False)
68146871
# Read uv_ffi min version from pyproject.toml once, in main process
68156872
_uv_ffi_min = "0.10.8.post2" # fallback
68166873
try:
@@ -6848,7 +6905,7 @@ def sync_interpreter(py_ver, target_exe):
68486905
needs_upgrade = True
68496906
if needs_upgrade:
68506907
subprocess.run([target_exe, "-m", "pip", "install", "--upgrade", "--no-cache-dir", "-q", f"uv_ffi>={_uv_ffi_min}"], capture_output=True, text=True, timeout=60, env=env)
6851-
return (py_ver, result.returncode == 0)
6908+
return (py_ver, _already_synced or result.returncode == 0)
68526909

68536910
with concurrent.futures.ThreadPoolExecutor(max_workers=6) as executor:
68546911
futures = [executor.submit(sync_interpreter, ver, exe) for ver, exe in sync_needed]

src/omnipkg/isolation/worker_daemon.py

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2732,7 +2732,7 @@ def execute_shm_task(
27322732
code: str,
27332733
shm_in: Dict[str, Any],
27342734
shm_out: Dict[str, Any],
2735-
timeout: float = 30.0,
2735+
timeout: float = 240.0,
27362736
) -> Dict[str, Any]:
27372737
"""Execute task with timeout."""
27382738
with self.lock:
@@ -3766,25 +3766,19 @@ def _run_cli(self, req, conn):
37663766
try:
37673767
pool = self.idle_pools.get(python_exe)
37683768
if pool:
3769-
# Prefer warm workers — scan pool for one with _is_warm set
3769+
# Drain pool, prefer a warm worker, put the rest back
37703770
all_workers = []
3771-
warm_worker = None
37723771
while not pool.empty():
37733772
try:
3774-
w = pool.get_nowait()
3775-
if getattr(w, "_is_warm", False) and warm_worker is None:
3776-
warm_worker = w
3777-
else:
3778-
all_workers.append(w)
3773+
all_workers.append(pool.get_nowait())
37793774
except: break
3780-
# Put non-selected workers back
3781-
selected = warm_worker or (all_workers.pop(0) if all_workers else None)
3775+
# Sort: warm first
3776+
all_workers.sort(key=lambda w: 0 if getattr(w, "_is_warm", False) else 1)
3777+
selected = all_workers.pop(0) if all_workers else None
3778+
# Return unselected workers to pool
37823779
for w in all_workers:
37833780
try: pool.put_nowait(w)
37843781
except: pass
3785-
if warm_worker:
3786-
try: pool.put_nowait(warm_worker) # temp
3787-
except: pass
37883782
worker = selected
37893783
if worker is None: raise queue.Empty()
37903784
safe_print(f"[RUN-CLI] got idle worker pid={worker.process.pid}", file=sys.stderr)
@@ -3812,7 +3806,7 @@ def _run_cli(self, req, conn):
38123806
safe_print(f"[RUN-CLI] worker acquired in {(_t_got_worker-_t0)*1000:.2f}ms", file=sys.stderr)
38133807

38143808
try:
3815-
safe_print(f"[RUN-CLI] sending req to worker", file=sys.stderr)
3809+
safe_print(f"[RUN-CLI] sending req to worker pid={worker.process.pid}", file=sys.stderr)
38163810
worker.process.stdin.write(json.dumps(req) + "\n")
38173811
worker.process.stdin.flush()
38183812

@@ -4006,10 +4000,13 @@ def _run_cli(self, req, conn):
40064000
pass
40074001
if worker is not None:
40084002
worker.force_shutdown()
4009-
# Only replenish if pool is below configured target
4003+
# Only replenish if pool is STILL below configured target after recycling.
4004+
# Give recycled worker a moment to land in the queue before checking.
40104005
_target = self.idle_config.get(python_exe, 0)
4011-
if self.idle_pools[python_exe].qsize() < _target:
4012-
self._replenish_idle_pool(python_exe)
4006+
if _target > 0:
4007+
time.sleep(0.02) # let recycle put_nowait propagate
4008+
if self.idle_pools[python_exe].qsize() < _target:
4009+
self._replenish_idle_pool(python_exe)
40134010

40144011
def _run_uv(self, req: dict, conn: socket.socket):
40154012
"""
@@ -4317,7 +4314,7 @@ def _execute_code(
43174314
code,
43184315
shm_in,
43194316
shm_out,
4320-
timeout=60.0,
4317+
timeout=worker_info.get("task_timeout", int(os.environ.get("OMNIPKG_WORKER_TIMEOUT", 360))),
43214318
)
43224319
# ── IMMEDIATE EVICTION IF NESTED SWITCHING OCCURRED ────────
43234320
_has_tag = bool(worker_tag)
@@ -4359,7 +4356,7 @@ def _execute_code(
43594356
code,
43604357
shm_in,
43614358
shm_out,
4362-
timeout=60.0,
4359+
timeout=worker_info.get("task_timeout", int(os.environ.get("OMNIPKG_WORKER_TIMEOUT", 360))),
43634360
)
43644361
return result
43654362
except Exception as e:
@@ -4451,7 +4448,7 @@ def _execute_code(
44514448
code,
44524449
shm_in,
44534450
shm_out,
4454-
timeout=60.0,
4451+
timeout=worker_info.get("task_timeout", int(os.environ.get("OMNIPKG_WORKER_TIMEOUT", 360))),
44554452
)
44564453
return result
44574454
except Exception as e:

0 commit comments

Comments
 (0)