Skip to content

Commit e4feb5d

Browse files
committed
Modify qedc_init call to eliminate import error with cudaq
If benchmark launched as benchmark_name/<bn>_benchmark.py, if fails to find the module. If launched using -m <benahmark_name>.<bn>_benchmark it would succeed. This code normalizes the sys.path stack so both work the same. (may only be needed whrn running inside cudaq container)
1 parent fe7a844 commit e4feb5d

File tree

1 file changed

+68
-3
lines changed

1 file changed

+68
-3
lines changed

_common/qedc_init.py

Lines changed: 68 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -143,12 +143,12 @@
143143
from importlib import import_module
144144
import sys
145145
from pathlib import Path
146+
from importlib import invalidate_caches
146147
from contextlib import contextmanager
147148

148149
# Store project root for use in path calculations
149150
_project_root = Path(__file__).parent.parent.resolve()
150151

151-
152152
@contextmanager
153153
def isolated_import_context(benchmark_name: str, api: str):
154154
"""
@@ -204,6 +204,35 @@ def isolated_import_context(benchmark_name: str, api: str):
204204
# This ensures we don't leave the import system in a broken state
205205
sys.path = original_sys_path
206206

207+
"""
208+
Alternate version of above, that should avoid the need for invalidate on cudaq, but doesn't.
209+
DEVNOTE: to be investigated.
210+
211+
@contextmanager
212+
def isolated_import_context(benchmark_name: str, api: str):
213+
original_sys_path = sys.path.copy()
214+
215+
project_root = str(_project_root) # e.g. .../QC-App-Oriented-Benchmarks-develop
216+
benchmark_dir = str(_project_root / benchmark_name) # e.g. .../bernstein_vazirani
217+
api_dir = str(_project_root / benchmark_name / api)
218+
219+
try:
220+
# Start from current sys.path but drop only the pieces that cause shadowing
221+
cleaned = [p for p in sys.path if p not in {'', '.', api_dir}]
222+
# (Optional) also drop the benchmark dir if you want to be extra safe:
223+
# cleaned = [p for p in cleaned if p != benchmark_dir]
224+
225+
# Critically: ensure the repo root is present and first,
226+
# so 'import bernstein_vazirani' always resolves like `python -m`.
227+
if project_root in cleaned:
228+
cleaned.remove(project_root)
229+
cleaned.insert(0, project_root)
230+
231+
sys.path = cleaned
232+
yield
233+
finally:
234+
sys.path = original_sys_path
235+
"""
207236

208237
def qedc_benchmarks_init(api: str, benchmark_name: str, module_names: list[str] = None) -> None:
209238
"""
@@ -246,10 +275,16 @@ def qedc_benchmarks_init(api: str, benchmark_name: str, module_names: list[str]
246275
if module_names is None:
247276
module_names = []
248277

278+
#from importlib.util import find_spec
279+
#assert find_spec("bernstein_vazirani") is not None, "anchor package not importable"
280+
249281
# Use context manager to temporarily modify sys.path during imports
250282
# This prevents local qiskit/ and _common/ folders from shadowing real packages
251283
with isolated_import_context(benchmark_name, api):
252-
284+
285+
if api == "cudaq":
286+
reset_module_caches(api, benchmark_name, module_names)
287+
253288
# Dynamically load each requested kernel module
254289
for module_name in module_names:
255290
# Check if already loaded to avoid redundant imports
@@ -279,4 +314,34 @@ def qedc_benchmarks_init(api: str, benchmark_name: str, module_names: list[str]
279314
sys.modules["execute"] = module
280315

281316
# Context manager automatically restores sys.path here
282-
# Subsequent imports in the benchmark program work normally
317+
# Subsequent imports in the benchmark program work normally
318+
319+
320+
def reset_module_caches (api: str, benchmark_name: str, module_names: list[str] = None) -> None:
321+
"""
322+
Reset the module caches to normalize execution.
323+
324+
This unfortunate block of code seems to be required only for cudaq.
325+
If benchmark launched as benchmark_name/<bn>_benchmark.py, if fails to find the module.
326+
If launched using -m <benahmark_name>.<bn>_benchmark it would succeed.
327+
This code normalizes the sys.path stack so both work the same.
328+
"""
329+
#assert find_spec("bernstein_vazirani") is not None, "anchor package not importable"
330+
331+
# 1) Normalize sys.path so top-level package import works like `-m`
332+
repo_root = str(Path(__file__).resolve().parents[1]) # repo root
333+
pkg_dir = str(Path(repo_root) / benchmark_name) # e.g. .../bernstein_vazirani
334+
sys.path = [repo_root] + [p for p in sys.path if p not in (repo_root, pkg_dir)]
335+
336+
# 2) Remove poisoned/partial module entries (this fixes __spec__ is None)
337+
to_clear = {benchmark_name,
338+
f"{benchmark_name}.{api}"} | {
339+
f"{benchmark_name}.{api}.{m}" for m in module_names}
340+
for name in to_clear:
341+
sys.modules.pop(name, None)
342+
343+
# 3) Refresh import caches
344+
invalidate_caches()
345+
346+
#assert find_spec("bernstein_vazirani") is not None, "anchor package not importable"
347+

0 commit comments

Comments
 (0)