Skip to content

Commit 378ba85

Browse files
committed
Fix atomic CAS function calls and improve installation fallback handling
- Fixed atomic function calls: cas_version → cas64 to match C extension API - Added verbose flag check for atomic module loading messages - Improved Time Machine fallback for legacy package installations - Added verification step before bubbling packages - Fixed uninstall crash when package metadata has invalid Name field - Enhanced interpreter sync detection for conflicting omnipkg installs - Bumped version to 2.1.3
1 parent 4794d70 commit 378ba85

3 files changed

Lines changed: 122 additions & 25 deletions

File tree

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
55

66
[project]
77
name = "omnipkg"
8-
version = "2.1.2"
8+
version = "2.1.3"
99
authors = [
1010
{ name = "1minds3t", email = "1minds3t@proton.me" },
1111
]

src/omnipkg/core.py

Lines changed: 108 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3221,17 +3221,65 @@ def install_and_verify(
32213221
safe_print(f" - 🏗️ Staging install for {package_name}=={version}...")
32223222

32233223
# 1. Install to staging
3224-
return_code, stdout = self.parent_omnipkg._run_pip_install(
3224+
return_code, install_output = self.parent_omnipkg._run_pip_install(
32253225
[f"{package_name}=={version}"],
32263226
target_directory=staging_path,
32273227
force_reinstall=True,
32283228
index_url=index_url,
32293229
extra_index_url=extra_index_url,
32303230
)
3231-
3232-
if return_code != 0:
3233-
safe_print(" ❌ Pip install failed in staging area.")
3234-
return False
3231+
3232+
verification_passed = False
3233+
if return_code == 0:
3234+
safe_print(" - 🧪 Running SMART import verification...")
3235+
try:
3236+
from .installation.verification_strategy import verify_bubble_with_smart_strategy
3237+
from .package_meta_builder import omnipkgMetadataGatherer
3238+
except ImportError:
3239+
from omnipkg.installation.verification_strategy import verify_bubble_with_smart_strategy
3240+
from omnipkg.package_meta_builder import omnipkgMetadataGatherer
3241+
3242+
gatherer = omnipkgMetadataGatherer(
3243+
config=self.parent_omnipkg.config,
3244+
env_id=self.parent_omnipkg.env_id,
3245+
omnipkg_instance=self.parent_omnipkg,
3246+
target_context_version=python_context_version,
3247+
)
3248+
existing_bubble_paths = self._find_dependency_bubbles(package_name, destination_path.parent)
3249+
3250+
verification_passed = verify_bubble_with_smart_strategy(
3251+
self.parent_omnipkg, package_name, version, staging_path, gatherer,
3252+
existing_bubble_paths=existing_bubble_paths
3253+
)
3254+
3255+
# --- TIME MACHINE TRIGGER ---
3256+
# Trigger if the initial modern install fails OR if its verification fails
3257+
if not verification_passed:
3258+
safe_print("\n" + "=" * 60)
3259+
if return_code != 0:
3260+
safe_print(f"🕰️ TIME MACHINE: Modern install failed for {package_name}=={version}.")
3261+
else:
3262+
safe_print(f"🕰️ TIME MACHINE: Verification failed for {package_name}=={version}, indicating dependency mismatch.")
3263+
safe_print(" - Attempting to rebuild from the past using historical dependencies...")
3264+
safe_print("=" * 60)
3265+
3266+
# Clean staging area before retry
3267+
shutil.rmtree(staging_path)
3268+
staging_path.mkdir(exist_ok=True)
3269+
3270+
historical_success = self.parent_omnipkg._run_historical_install_fallback(
3271+
package_name,
3272+
version,
3273+
target_directory_override=staging_path,
3274+
index_url=index_url,
3275+
extra_index_url=extra_index_url,
3276+
)
3277+
3278+
if not historical_success:
3279+
safe_print(f" ❌ TIME MACHINE: Historical rebuild failed for {package_name}=={version}.")
3280+
return False
3281+
3282+
safe_print(f"\n ✅ TIME MACHINE: Successfully rebuilt {package_name}=={version} into staging area.")
32353283

32363284
# 2. Find already-created bubbles for dependencies
32373285
safe_print(" - 🔍 Locating dependency bubbles for verification...")
@@ -6105,6 +6153,20 @@ def _check_sync_status_ultra_fast(self, master_version: str) -> List[Tuple[str,
61056153
pass
61066154

61076155
# If none of the expected patterns exist, sync is needed
6156+
# First, check for ANY installed omnipkg metadata. This is the ground truth.
6157+
conflicting_installs = [
6158+
p for p in site_packages.glob("omnipkg-*.dist-info")
6159+
if p.name != expected_dist_info and p.name != expected_editable_dist_info
6160+
]
6161+
6162+
# If we found an old/conflicting .dist-info directory, sync is ALWAYS needed.
6163+
# This is non-negotiable and overrides any .pth file check.
6164+
if conflicting_installs:
6165+
sync_needed.append((py_ver, str(exe_path)))
6166+
continue # Move to the next interpreter
6167+
6168+
# Only if there are no conflicts, we check if an install exists at all.
6169+
# If none of the expected patterns exist, sync is needed.
61086170
if not (has_regular_install or has_editable_install or has_pth_install):
61096171
sync_needed.append((py_ver, str(exe_path)))
61106172

@@ -12932,17 +12994,24 @@ def smart_uninstall(
1293212994
safe_print(_("🤷 No versions selected for uninstallation."))
1293312995
continue
1293412996

12935-
final_to_uninstall = [
12936-
item
12937-
for item in to_uninstall
12938-
if not (
12939-
item.get("install_type") == "active"
12940-
and (
12941-
canonicalize_name(item.get("Name")) in core_deps
12942-
or canonicalize_name(item.get("Name")) == "omnipkg"
12943-
)
12944-
)
12945-
]
12997+
final_to_uninstall = []
12998+
for item in to_uninstall:
12999+
item_name = item.get("Name")
13000+
13001+
# Skip items with invalid/missing names
13002+
if not item_name or not isinstance(item_name, str):
13003+
safe_print(f"⚠️ Skipping item with invalid Name: {item}")
13004+
continue
13005+
13006+
c_item_name = canonicalize_name(item_name)
13007+
13008+
# Skip protected packages
13009+
if item.get("install_type") == "active" and (
13010+
c_item_name in core_deps or c_item_name == "omnipkg"
13011+
):
13012+
continue
13013+
13014+
final_to_uninstall.append(item)
1294613015

1294713016
if len(final_to_uninstall) != len(to_uninstall):
1294813017
safe_print(_("⚠️ Skipped one or more protected core packages."))
@@ -14522,6 +14591,29 @@ def _run_pip_install(
1452214591
self._auto_heal_invalid_distributions(full_output, cleanup_path)
1452314592

1452414593
if return_code != 0:
14594+
# --- TIME MACHINE FALLBACK ---
14595+
# Check for legacy build system failure (setuptools incompatibility)
14596+
is_legacy_failure = "metadata-generation-failed" in full_output.lower()
14597+
if is_legacy_failure and packages and "==" in packages[0]:
14598+
pkg_name, pkg_ver = self._parse_package_spec(packages[0])
14599+
safe_print("\n" + "=" * 60)
14600+
safe_print(f"🕰️ TIME MACHINE: Detected legacy build failure for {pkg_name}=={pkg_ver}.")
14601+
safe_print(" - This is common for old packages with modern build tools.")
14602+
safe_print("=" * 60)
14603+
14604+
if self._run_historical_install_fallback(
14605+
pkg_name,
14606+
pkg_ver,
14607+
target_directory_override=target_directory,
14608+
index_url=index_url,
14609+
extra_index_url=extra_index_url,
14610+
):
14611+
safe_print(f"\n ✅ TIME MACHINE: Successfully rebuilt {pkg_name}=={pkg_ver} from the past.")
14612+
return 0, captured_output # Return success!
14613+
else:
14614+
safe_print(f"\n ❌ TIME MACHINE: Failed to rebuild {pkg_name}=={pkg_ver}. The original error follows.")
14615+
# Fall through to return the original error code below
14616+
1452514617
# Check for "no compatible version" error
1452614618
no_dist_found = (
1452714619
"no matching distribution found" in full_output.lower()

src/omnipkg/isolation/worker_daemon.py

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,16 @@
3636
try:
3737
from omnipkg.isolation import omnipkg_atomic
3838
_HAS_ATOMICS = True
39-
# Print to stderr so it shows up in daemon logs
40-
sys.stderr.write(_('✅ [DAEMON] Hardware Atomics LOADED: {}\n').format(omnipkg_atomic))
39+
# Directly check command-line args. This is clean and has no side effects.
40+
if "--verbose" in sys.argv or "-V" in sys.argv:
41+
sys.stderr.write(_('✅ [DAEMON] Hardware Atomics LOADED: {}\n').format(omnipkg_atomic))
4142
except ImportError as e:
4243
_HAS_ATOMICS = False
43-
sys.stderr.write(_('⚠️ [DAEMON] Hardware Atomics FAILED: {}\n').format(e))
44-
# Debug: print sys.path to see where it's looking
45-
sys.stderr.write(f" sys.path: {sys.path}\n")
44+
# Only show the failure warning if the user explicitly asks for verbose output.
45+
if "--verbose" in sys.argv or "-V" in sys.argv:
46+
sys.stderr.write(_('⚠️ [DAEMON] Hardware Atomics FAILED: {}\n').format(e))
47+
# Also keep the helpful debug info inside the verbose check.
48+
sys.stderr.write(f" sys.path: {sys.path}\n")
4649

4750
# ═══════════════════════════════════════════════════════════════
4851
# 0. CONSTANTS & UTILITIES
@@ -162,7 +165,7 @@ def acquire_atomic_spinlock(self, timeout_seconds: float = 5.0) -> int:
162165

163166
# 2. Atomic Attempt: Even -> Odd
164167
# If successful, we own the lock!
165-
if omnipkg_atomic.cas_version(addr, current, current + 1):
168+
if omnipkg_atomic.cas64(addr, current, current + 1): # ← FIXED!
166169
return current + 1
167170

168171
# CAS failed (contention). Backoff slightly.
@@ -177,7 +180,7 @@ def release_atomic_spinlock(self, my_odd_version: int):
177180
addr = ctypes.addressof(c_obj)
178181

179182
# Verify we still own it (sanity check)
180-
if not omnipkg_atomic.cas_version(addr, my_odd_version, my_odd_version + 1):
183+
if not omnipkg_atomic.cas64(addr, my_odd_version, my_odd_version + 1): # ← FIXED!
181184
# Should never happen if logic is sound
182185
sys.stderr.write("CRITICAL: Atomic release failed (Version Mismatch)!\n")
183186

@@ -269,6 +272,7 @@ def recv_json(sock: socket.socket, timeout: float = 30.0) -> dict:
269272
raise ConnectionResetError("Socket stream interrupted.")
270273
data_buffer.extend(chunk)
271274
return json.loads(data_buffer.decode("utf-8"))
275+
272276
class UniversalGpuIpc:
273277
"""
274278
Pure CUDA IPC using ctypes - works WITHOUT PyTorch!
@@ -426,6 +430,7 @@ def __init__(self, ptr, shape, typestr):
426430
CUDABuffer(final_ptr, data["shape"], data["typestr"]),
427431
device=f"cuda:{data['device']}",
428432
)
433+
429434
class SHMRegistry:
430435
"""Track and cleanup orphaned shared memory blocks."""
431436

@@ -2981,7 +2986,7 @@ def optimistic_update_atomic(self, expected_version: int) -> bool:
29812986
# Call C extension
29822987
# Atomically: if version == expected, set version = expected + 1
29832988
# We skip the "Lock" state entirely because CAS *is* the lock.
2984-
success = omnipkg_atomic.cas_version(addr, expected_version, expected_version + 1)
2989+
success = omnipkg_atomic.cas64(addr, expected_version, expected_version + 1) # ← FIXED!
29852990

29862991
return success
29872992

0 commit comments

Comments
 (0)