AttributeError: 'NoneType' object has no attribute 'startswith' #8651
-
Hi all, I have gotten issue reports in a program that i help maintain called Decky that is a plugin injector and manager for Steam, specifically meant for the Steam Deck. The architecture of this application is pretty weird, so it's possible the following issue is caused by us. Note: this seems to be a windows-specific issue, and i cannot reproduce it locally. It may be related to a recent windows update. I have no idea where to start to figure out this issue.
Link to source file of the stack trace that's in our codebase # Line that produces the above stack trace
from .imports import decky
|
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 3 replies
-
Everyday we learn something new; unfortunately around here, that something usually translates into something horrible... As the traceback suggests, the problem is that the pyinstaller/PyInstaller/loader/pyimod02_importers.py Lines 274 to 276 in 09ae973 We should probably add a guard against that, similarly to how built-in finders also guard against non-string entries (e.g., here). But how did we end up with It turns out that the problem is indeed a self-inflicted one, and boils down to this part of your entry-point script: # Append the system and user python paths
sys.path.extend(get_system_pythonpaths()) This ends up appending empty string ( Your plugins' code is executed in A slightly-related intermission: binding To illustrate: you bind from sys import exit, path as syspath, modules as sysmodules And later on, in line 81 of sandboxed_plugin.py (within the syspath.append(path.join(environ["DECKY_PLUGIN_DIR"], "py_modules")) And yet, when you do the import in line 88 that triggers the error, the Why? Because the binding in line 6 happens when The worker subprocess codepath then diverges in line 228, where This ends up reading the values that are passed to the worker process from the parent, which replaces Now, let us return to that def get_system_pythonpaths() -> list[str]:
try:
# run as normal normal user if on linux to also include user python paths
proc = subprocess.run(["python3" if localplatform.ON_LINUX else "python", "-c", "import sys; print('\\n'.join(x for x in sys.path if x))"],
# TODO make this less insane
capture_output=True, user=localplatform.localplatform._get_user_id() if localplatform.ON_LINUX else None, env={} if localplatform.ON_LINUX else None) # pyright: ignore [reportPrivateUsage]
return [x.strip() for x in proc.stdout.decode().strip().split("\n")]
except Exception as e:
logger.warn(f"Failed to execute get_system_pythonpaths(): {str(e)}")
return [] This fails to check for return code of the process it attempts to spawn, so if If Which is obviously a bad idea for the frozen application, because that python environment might be using completely different python version than the one that application was built with. And lastly, this: def main():
setproctitle(f"Decky Loader {get_loader_version()} ({getproctitle()})")
setthreadtitle("Decky Loader")
if ON_WINDOWS:
# Fix windows/flask not recognising that .js means 'application/javascript'
import mimetypes
mimetypes.add_type('application/javascript', '.js')
# Required for multiprocessing support in frozen files
multiprocessing.freeze_support()
else:
if get_effective_user_id() != 0:
logger.warning(f"decky is running as an unprivileged user, this is not officially supported and may cause issues") You should probably (re)read https://pyinstaller.org/en/stable/common-issues-and-pitfalls.html#multi-processing, otherwise you might be in for a rude surprise if you ever try to build for macOS, or if you ever end up building with python 3.14 for linux. Also, if it works for you, you might want consider to moving that |
Beta Was this translation helpful? Give feedback.
Everyday we learn something new; unfortunately around here, that something usually translates into something horrible...
As the traceback suggests, the problem is that the
path
list passed toPyiFrozenImport.find_spec
contains aNone
, so this part raises an error:pyinstaller/PyInstaller/loader/pyimod02_importers.py
Lines 274 to 276 in 09ae973
We should probably add a guard against that, similarly to how built-in finders also guard against non-string entries (e.g., here).
But how did we end up with
None
in that list in the first place?It turns out that the problem is indeed a self-inflicted one, and boi…