@@ -241,6 +241,41 @@ def register_repl(self, *, name: str, runner: Callable[[ReplContext], int]) -> N
241241 self ._services .hook_registry .register_repl (name = name , runner = runner , ext_name = self ._ext_name )
242242
243243
244+ def _extension_search_roots () -> List [str ]:
245+ """Return directories to search for the interpreter's built-in `ext/`.
246+
247+ When running as a frozen executable (e.g. PyInstaller), `__file__` points to
248+ the extracted bundle, but users often ship extensions alongside the exe.
249+ Prefer the exe directory, then the bundled extraction dir, then source dir.
250+ """
251+
252+ roots : List [str ] = []
253+ if getattr (sys , "frozen" , False ):
254+ roots .append (os .path .dirname (os .path .abspath (sys .executable )))
255+ meipass = getattr (sys , "_MEIPASS" , None )
256+ if isinstance (meipass , str ) and meipass :
257+ roots .append (os .path .abspath (meipass ))
258+ roots .append (os .path .dirname (os .path .abspath (__file__ )))
259+ # de-dup while preserving order
260+ out : List [str ] = []
261+ seen : set [str ] = set ()
262+ for r in roots :
263+ if r not in seen :
264+ out .append (r )
265+ seen .add (r )
266+ return out
267+
268+
269+ def _resolve_in_builtin_ext (path : str ) -> Optional [str ]:
270+ if os .path .isabs (path ):
271+ return None
272+ for root in _extension_search_roots ():
273+ candidate = os .path .join (root , "ext" , path )
274+ if os .path .exists (candidate ):
275+ return os .path .abspath (candidate )
276+ return None
277+
278+
244279def _unique_module_name (path : str ) -> str :
245280 base = os .path .basename (path )
246281 digest = hashlib .sha256 (os .path .abspath (path ).encode ("utf-8" )).hexdigest ()[:12 ]
@@ -253,9 +288,9 @@ def load_extension_module(path: str) -> Any:
253288 # interpreter's own `ext` directory as a fallback before failing.
254289 if not os .path .exists (path ):
255290 if not os .path .isabs (path ):
256- interpreter_ext = os . path . join ( os . path . dirname ( os . path . abspath ( __file__ )), "ext" , path )
257- if os . path . exists ( interpreter_ext ) :
258- path = interpreter_ext
291+ resolved = _resolve_in_builtin_ext ( path )
292+ if resolved is not None :
293+ path = resolved
259294 else :
260295 raise ASMExtensionError (f"Extension not found: { path } " )
261296 else :
@@ -309,22 +344,22 @@ def read_asmx(pointer_file: str) -> List[str]:
309344 # Resolve relative entries: prefer pointer-file base dir, then
310345 # cwd, then interpreter's ext/ directory as a final fallback.
311346 candidate = line
312- if not os .path .isabs (candidate ):
347+ if not os .path .isabs (candidate ): # relative path
313348 candidate_base = os .path .abspath (os .path .join (base_dir , candidate ))
314- if os .path .exists (candidate_base ):
349+ if os .path .exists (candidate_base ): # found relative to pointer file
315350 out .append (candidate_base )
316351 continue
317352 candidate_cwd = os .path .abspath (candidate )
318- if os .path .exists (candidate_cwd ):
353+ if os .path .exists (candidate_cwd ): # found relative to cwd
319354 out .append (candidate_cwd )
320355 continue
321- interpreter_ext = os . path . join ( os . path . dirname ( os . path . abspath ( __file__ )), "ext" , candidate )
322- if os . path . exists ( interpreter_ext ) :
323- out .append (os . path . abspath ( interpreter_ext ) )
356+ resolved = _resolve_in_builtin_ext ( candidate )
357+ if resolved is not None :
358+ out .append (resolved )
324359 continue
325360 raise ASMExtensionError (f"Extension referenced in { pointer_file } not found: { candidate } " )
326- else :
327- if os .path .exists (candidate ):
361+ else : # absolute path
362+ if os .path .exists (candidate ): # found as absolute path
328363 out .append (os .path .abspath (candidate ))
329364 else :
330365 raise ASMExtensionError (f"Extension referenced in { pointer_file } not found: { candidate } " )
@@ -345,9 +380,9 @@ def gather_extension_paths(paths: Sequence[str]) -> List[str]:
345380 # the current working directory, try the interpreter's `ext`
346381 # directory as a fallback.
347382 if not os .path .isabs (p ) and not os .path .exists (p ):
348- alt = os . path . join ( os . path . dirname ( os . path . abspath ( __file__ )), "ext" , p )
349- if os . path . exists ( alt ) :
350- expanded .append (os . path . abspath ( alt ) )
383+ resolved = _resolve_in_builtin_ext ( p )
384+ if resolved is not None :
385+ expanded .append (resolved )
351386 continue
352387 expanded .append (p )
353388 # normalize
0 commit comments