You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Python 3.11.0 -> 3.11.1 : ...\Python311\DLLs not added to sys.path in embedded startup on Windows #100320
When updating Python 3.11.0 -> 3.11.1 (or reverse to reverse the issue), the ...\Python311\DLLs folder is suddenly not added anymore to sys.path in embedded startup due to somehow impaired search path algorithm. E.g. in Pythonwin.exe. And thus things like ctypes, socket, hashlib etc. cannot be imported. But ...\Python311\Lib and all the other paths are correctly there, just the DLLs path missing.
The issue is also in current Py 3.12.0a3 at least.
The issue seems not to be with python.exe startup.
The issue also disappears when I monkey-copy the Pythonwin.exe next to python.exe and use that copy.
Note: Pythonwin.exe locates pythonNN.dll dynamically and does the usual Python init.
And extra confusing: in the registry there is a PythonPath key like C:\Python312\Lib\;C:\Python312\DLLs\ :
I always thought that the DLLs path is taken from there. But when I edit-damage that like C:\Python312\Lib\;C:\Python312\DLLsx\ e.g., it has no effect :-)
The correct DLLs dir (only) is still in the sys.path in the above working cases, and DLLsx also does not appear on sys.path in the non-working cases.
Reproduce:
Win10
pip install pywin32; run Pythonwin.exe
import ctypes and/or inspect sys.path after start.
When I symlink or copy the Python3.12 or 3.11.1 python.exe + pythonNN.dll somewhere else, e.g. in a \bin folder, this moved python.exe (or renamed pythonNN.exe) doesn't work anymore. This worked before, and does so still for 3.10- at least.
A long error:
Could not find platform independent libraries <prefix>
Python path configuration:
PYTHONHOME = (not set)
PYTHONPATH = (not set)
program name = 'python312.exe'
isolated = 0
environment = 1
user site = 1
safe_path = 0
import site = 1
is in build tree = 0
stdlib dir = 'C:\bin\Lib'
sys._base_executable = 'C:\\bin\\python312.exe'
sys.base_prefix = 'C:\\bin'
sys.base_exec_prefix = 'C:\\bin'
sys.platlibdir = 'DLLs'
sys.executable = 'C:\\bin\\python312.exe'
sys.prefix = 'C:\\bin'
sys.exec_prefix = 'C:\\bin'
sys.path = [
'C:\\bin\\python312.zip',
'C:\\bin\\Lib',
'C:\\bin',
]
Fatal Python error: init_fs_encoding: failed to get the Python codec of the filesystem encoding
Python runtime state: core initialized
ModuleNotFoundError: No module named 'encodings'
Current thread 0x00002f68 (most recent call first):
<no Python frame>
Interesting also here is the line
sys.platlibdir = 'DLLs'
which is the only entry with non-absolute path, thus "double wrong" - which may point back to the initial error issue.
When I symlink or copy the Python3.12 or 3.11.1 python.exe + pythonNN.dll somewhere else, e.g. in a \bin folder, this moved python.exe (or renamed pythonNN.exe) doesn't work anymore.
Prior to 3.11, if Python's home directory can't be determined, then the default value of the "PythonPath" key in the registry gets added to sys.path. For some reason this was dropped from the new implementation in 3.11+. It's still the documented behavior, however:
If a Python home is found, the relevant sub-directories added to sys.path (Lib, plat-win, etc) are based on that folder. Otherwise, the core Python path is constructed from the PythonPath stored in the registry.
sys.platlibdir = 'DLLs'
which is the only entry with non-absolute path, thus "double wrong" - which may point back to the initial error issue.
The value of sys.platlibdir is used in the platform library landmark. On Windows, this landmark is just "{platlibdir}", i.e. "DLLs". It's looked for in the exec_prefix directory, which on Windows is the directory of the 'real' executable. In fact, this is not necessarily the real path of the executable because the realpath() function that's used in "Modules/getpath.py" does nothing on Windows. (This is because neither _Py_wreadlink() nor _Py_wrealpath() is implemented on Windows.) Thus if the executable path is a symlink, then exec_prefix is initially the directory of the symlink. This is unlikely to contain the "DLLs" landmark directory (i.e. it's unlikely that "DLLs" is linked as well).
If the platform library landmark isn't found, then both exec_prefix and the platform library directory are set to prefix. This is where the standard library "Lib" directory is supposed to be. But if the interpreter DLL and executable are symlinks, then the landmark for the standard library ("Lib\os.py") is also unlikely to be found (i.e. it's unlikely that "Lib" is linked as well). In this case, the values of prefix, exec_prefix, and the platform library are set to the current working directory, and the standard library path "{prefix}\Lib" is added to sys.path even if it doesn't exist.
So I guess this needs to be re-established at priority due to a bug introduced from 3.11.0 to .1 :
a)
Otherwise, the core Python path is constructed from the PythonPath stored in the registry.
This will solve both issues above (embedded.exe w dynamic loading of python DLL + linked/copied python.exe & .dll) at least as far as to regain the previous function and not break things.
In addition, reading the description and the linked doc regarding sys.path initialization (on Windows), the following may be reasonable to add / change in the logic for making it more robust and smart:
Regarding,
If the environment variable PYTHONHOME is set, it is assumed as “Python Home”. Otherwise, the path of the main Python executable is used to locate a “landmark file” (either Lib\os.py or pythonXY.zip) to deduce the “Python Home”. If a Python home is found, the relevant sub-directories added to sys.path (Lib, plat-win, etc) are based on that folder. Otherwise, the core Python path is constructed from the PythonPath stored in the registry.
b)
after trying the executable location as Python Home (can be a embedding.exe dynamically using the python DLL from a python installation) and before trying the fallback registry entries, also the python DLL location should be tried (when existing and different), because that is a stronger indicator for a specific python installation, which may in some cases be different from the registry entry.
c)
In Python 2 when the pythonNN.dll was in system32 for a system install, it was enough to put a link/copy of the only python.exe to e.g. C:\bin\python(NN).exe on the PATH (not for the python DLL) for handy short cut. Of course there are several good reasons to not use the system32 folder. But to regain that function, python.exe could become worth its salt and dynamically locate the python DLL (relative & symlink aware + from registry) regarding Py_Main, similar to Pythonwin.exe, instead of having a hard dependency at Windows link level on pythonNN.dll .
d)
The symlink / real realpath should be used on Windows as well (at priority before registry entry) regarding detection of the most relevant Python Home. (Of course respecting a nearby pyvenv.cfg before dereferencing the symlink)
That also works only as far as it sits in the 'home' directory besides DLLs/ and Lib/, see
When I symlink or copy the Python3.12 or 3.11.1 python.exe + pythonNN.dll somewhere else, e.g. in a \bin folder, this moved python.exe (or renamed pythonNN.exe) doesn't work anymore ...
.. and explanations from eryksun and in the linked document.
Its obviously a bug which broke "Otherwise, the core Python path is constructed from the PythonPath stored in the registry." ( a) ) besides general weaknesses of the implemented logic, which may be improved by b) c) d) .
Like mmomtchev (linked issue/commit) I use 3.11.0 until the bug is hopefully fixed again in 3.11.2 . More are facing this bug.
Another workaround would also be possible, but I don't wont to raise memes which trigger adaptation to a bug in the middle of the road.
I came up with the following workaround for the time being:
import os
import os.path
if os.name == 'nt' and \
sys.version_info.major == 3 and \
sys.version_info.minor >= 11:
# Python 3.11 has a bug with DLLs directory missing from sys.path
# so add it if it's not there
base = sys.base_exec_prefix
dllspath = os.path.join(base, sys.platlibdir)
if os.path.exists(dllspath) and dllspath not in sys.path:
i = sys.path.index(base) if base in sys.path else len(sys.path)
sys.path.insert(i, dllspath)
By the way, Anaconda fork has a similar issue (ignoring PythonPath in registry) which prevents it from being used as embedded runtime: ContinuumIO/anaconda-issues#11374
We're also running into this issue. Has anyone figured out a good workaround? Also, how come python.exe works correctly?
Part of the change in 3.11.1 is that it no longer searches up from the executable directory to find exec_prefix, i.e. the directory that contains "DLLs". This change restores compatibility with 3.10 and earlier versions. Searching up would help with the Pythonwin case, but I agree that this is not a secure practice on Windows (e.g. with the default security, any authenticated user can create a directory in the root directory of a volume, including the system volume), and it was a mistake to enable this behavior in 3.11.0. The problem is better addressed in terms of the other changes in 3.11.1.
In 3.10 on Windows, exec_prefix and prefix are always the same, and "{exec_prefix}\DLLs" is added to sys.path whether or not it exists. In 3.11.1, exec_prefix is set to the executable directory and platstdlib_dir is set to "{exec_prefix}\DLLs" only if "DLLs" is found in the executable directory. Otherwise both exec_prefix and platstdlib_dir are set to the same directory as prefix.
I don't understand the cited reason for not setting platstdlib_dir to "{exec_prefix}\DLLs", as was always done in previous versions. The comment says that this "would give site-packages precedence over executable_dir". This might be related to another bug in Modules/getpath.py, in the following code:
# Then add stdlib_dir and platstdlib_dirifos_name=='nt'andvenv_prefix:
# QUIRK: Windows generates paths differently in a venvifplatstdlib_dir:
pythonpath.append(platstdlib_dir)
ifstdlib_dir:
pythonpath.append(stdlib_dir)
ifexecutable_dirnotinpythonpath:
pythonpath.append(executable_dir)
else:
ifstdlib_dir:
pythonpath.append(stdlib_dir)
ifplatstdlib_dir:
pythonpath.append(platstdlib_dir)
On Windows, executable_dir should always be added, and the order should always be platstdlib_dir, stdlib_dir, executable_dir. I verified this in versions 3.6-3.10, running with -S (no site import) for both the base environment and a virtual environment.
I think the above snippet should be modified to remove the venv_prefix check and always add executable_dir, which should never be the same as stdlib_dir or platstdlib_dir (see further modifications below). For example:
# Then add stdlib_dir and platstdlib_dirifos_name=='nt':
# QUIRK: Windows uses a different ordering, and it includes the# directory of the executable.ifplatstdlib_dir:
pythonpath.append(platstdlib_dir)
ifstdlib_dir:
pythonpath.append(stdlib_dir)
ifexecutable_dir:
pythonpath.append(executable_dir)
else:
ifstdlib_dir:
pythonpath.append(stdlib_dir)
ifplatstdlib_dir:
pythonpath.append(platstdlib_dir)
I'd modify the code that sets exec_prefix as follows:
# Detect exec_prefix by searching from executable for the platstdlib_dirifPLATSTDLIB_LANDMARKandnotexec_prefix:
ifexecutable_dir:
ifos_name=='nt':
# QUIRK: For compatibility and security, do not search for the# DLLs directory.ifisdir(joinpath(executable_dir, PLATSTDLIB_LANDMARK)):
exec_prefix=executable_direlse:
exec_prefix=search_up(executable_dir, PLATSTDLIB_LANDMARK, test=isdir)
ifnotexec_prefix:
ifEXEC_PREFIX:
exec_prefix=EXEC_PREFIX# QUIRK: On Windows, if DLLs isn't found in the executable# directory, don't warn. Just use the fallback that makes# exec_prefix the same as prefix, and assume that DLLs exists# there. If DLLs doesn't exist, an embedding application can place# extension modules in the directory of the executable, which is# always in sys.path ahead of site-packages.elifos_name!='nt':
warn('Could not find platform dependent libraries <exec_prefix>')
# Fallback: assume exec_prefix == prefixifnotexec_prefix:
exec_prefix=prefix
There's a lot of discussion here, but fundamentally it seems like the bug is the registry code not loading properly.
For the (IMHO, broken) scenario where you have a normal Python install but are trying to use it from a different executable, the registry key is how the stdlib should be found. The better layout is to not just copy the Python executable, but also the libraries that match it.1
We have preferred "adjacent to the executable" since 3.6-ish because it is necessary to be able to run a full copy of Python independent from a "real" install. Before that, you could have had a whole copy of 3.5.5 in one directory and an installed copy of 3.5.1 would break it because the registry key "won".
But we shouldn't be breaking the registry here. I'll take a look. I've not been convinced of any of the other changes that are mentioned.
Footnotes
Though it does seem like this is the safest possible approach that involves loading the DLL and initialising it. Could be better if the DLL was initialised properly and provided the paths it needs, but I know that's incredibly painful because... well... it's not how we want 3rd party apps using the Python runtime. Just bring your own copy of it, and don't try and do anything complicated with someone else's install, or else ask the user for the executable path and run it that way. ↩
Looks like I missed the case of reading directly from the PythonPath key, but got the read for all subkeys. The read of the top one only happens when home isn't known, which is how it slipped through all other validation, because there are very few scenarios where that occurs anymore.
Setting PYTHONHOME environment variable before loading the DLL ought to be a suitable workaround for 3.11.1 if needed (and setting that against 3.10 should also skip reading the registry). But I think we'll be okay with a fix for 3.11.2 to read from that key under these circumstances.
In any case, the change I just pushed should satisfy things. The only thing we're discussing now is whether DLLs should precede Lib in the case where DLLs is assumed to be the same as prefix because we couldn't find it. I think it should precede Lib, rather than switching back and forth, as I consider "location where our standard .pyd's live" to be the main reason we are including the directory at all. executable_dir being in there is considered a mistake that we can't fix, but it can at least remain at the end of the list when that's the only reason we have to include it.
if (wcscpy_s(buf, bufsz- (buf-start_buf), calculate->home)) {
returnINIT_ERR_BUFFER_OVERFLOW();
}
buf=wcschr(buf, L'\0');
p++;
n--;
}
wcsncpy(buf, p, n);
buf+=n;
*buf++=DELIM;
if (q==NULL) {
break;
}
p=q+1;
}
}
if (argv0_path) {
wcscpy(buf, argv0_path);
buf=wcschr(buf, L'\0');
*buf++=DELIM;
}
Thus it adds (f"{home}\\DLLs", f"{home}\\lib", f"{argv0_path}").
In a 3.10 virtual environment, argv0_path goes through a couple steps if the venv launcher is used. First it changes to the value of the environment variable __PYVENV_LAUNCHER__ in order to find "pyvenv.cfg". Then argv0_path is set to the "home" value in the venv configuration, which should be the base executable directory (venv gets the "home" value from splitting sys._base_executable).
In 3.10, sys.exec_prefix is always the same as sys.prefix (i.e. home). So the "DLLs" directory would always be found relative to prefix, if it's found at all. You changed it in 3.11 to allow using the executable directory for sys.exec_prefix if the "DLLs" landmark is found. I'm okay with that change. In 3.11.1, you modified it to no longer search the parent directories for the "DLLs" landmark, relative to the executable directory. I think it was the right decision. The search up strategy is not secure on Windows, both in terms of file security and layout.
Are there tests in CPython test suite which test embedded scenarios? If so, they should be extended with testing native module imports (like ctypes). It seems things got broken multiple times in the past because "python.exe works fine so it's all good".
Yes, but we don't test this scenario because it's not a supported one.
If you want to embed Python, embed all of it, or else configure the paths properly when you initialise it. Taking the executable and hoping it will find an existing install isn't the way.
assuming we want to rely on an existing full install, is there a howto/example of how to "configure the paths properly" which would not have been broken by this issue?
For a full install though, your entry point is python.exe, not pythonXY.dll. Directly using the DLL from an install you're not running inside of isn't a supported path - it's totally supported for you to embed the DLL into your own app, but not to "borrow" it from an install.
So the other way that wouldn't have been broken is to run python.exe out of the install, rather than your own executable. If there's something you need to do as part of initialization that requires you to use your own executable, then you most likely should be embedding a full copy of Python. (For comparison, Blender embeds a full copy of Python, because they need to do this, while OBS Studio merely adds search paths for their own modules and so can run through a regular python.exe.)
If there's something you need to do as part of initialization that requires you to use your own executable ...
Incidentally, I'd love to hear about these if you have them, because I haven't come across anything before that really needed it (without also needing to control the rest of the Python runtime, i.e. bring their own version). Not knowing makes it hard to factor in the scenarios when we're looking at what can/should/might change.
If there's something you need to do as part of initialization that requires you to use your own executable ...
Incidentally, I'd love to hear about these if you have them, because I haven't come across anything before that really needed it (without also needing to control the rest of the Python runtime, i.e. bring their own version). Not knowing makes it hard to factor in the scenarios when we're looking at what can/should/might change.
Igor spotted this issue in the context of IDAPython.
By design, IDAPython does not use python.exe at all, but rather loads the installed Python DLL/so/dylib, precisely so that the user can use whatever method they want to control the packages that are installed on their system (including distribution packages wherever supported.)
Igor spotted this issue in the context of IDAPython. By design, IDAPython does not use python.exe at all, but rather loads the installed Python DLL/so/dylib, precisely so that the user can use whatever method they want to control the packages that are installed on their system (including distribution packages wherever supported.)
I do this in my app as well. I don't see how I could possibly use the python executable, as I need the interpreter running inside the same address space as the rest of the code so I can have shared access to the same data.
Using an existing python library means the user can install Python and their packages any numbers of ways, apt, dnf, pip, msys2, anaconda, homebrew, etc.
Why would I want to get into the cross platform Python packaging and installation business?! That's crazy. And it's already been done.
Activity
kxrob commentedon Dec 17, 2022
An also:
When I symlink or copy the Python3.12 or 3.11.1
python.exe + pythonNN.dllsomewhere else, e.g. in a \bin folder, this movedpython.exe(or renamedpythonNN.exe) doesn't work anymore. This worked before, and does so still for 3.10- at least.A long error:
Interesting also here is the line
sys.platlibdir = 'DLLs'which is the only entry with non-absolute path, thus "double wrong" - which may point back to the initial error issue.
eryksun commentedon Dec 17, 2022
Prior to 3.11, if Python's home directory can't be determined, then the default value of the "PythonPath" key in the registry gets added to
sys.path. For some reason this was dropped from the new implementation in 3.11+. It's still the documented behavior, however:The value of
sys.platlibdiris used in the platform library landmark. On Windows, this landmark is just "{platlibdir}", i.e. "DLLs". It's looked for in theexec_prefixdirectory, which on Windows is the directory of the 'real' executable. In fact, this is not necessarily the real path of the executable because therealpath()function that's used in "Modules/getpath.py" does nothing on Windows. (This is because neither_Py_wreadlink()nor_Py_wrealpath()is implemented on Windows.) Thus if the executable path is a symlink, thenexec_prefixis initially the directory of the symlink. This is unlikely to contain the "DLLs" landmark directory (i.e. it's unlikely that "DLLs" is linked as well).If the platform library landmark isn't found, then both
exec_prefixand the platform library directory are set toprefix. This is where the standard library "Lib" directory is supposed to be. But if the interpreter DLL and executable are symlinks, then the landmark for the standard library ("Lib\os.py") is also unlikely to be found (i.e. it's unlikely that "Lib" is linked as well). In this case, the values ofprefix,exec_prefix, and the platform library are set to the current working directory, and the standard library path "{prefix}\Lib" is added tosys.patheven if it doesn't exist.exclude Python 3.11.1 from testing
kxrob commentedon Dec 20, 2022
So I guess this needs to be re-established at priority due to a bug introduced from 3.11.0 to .1 :
a)
This will solve both issues above (embedded.exe w dynamic loading of python DLL + linked/copied python.exe & .dll) at least as far as to regain the previous function and not break things.
In addition, reading the description and the linked doc regarding sys.path initialization (on Windows), the following may be reasonable to add / change in the logic for making it more robust and smart:
Regarding,
b)
after trying the executable location as Python Home (can be a
embedding.exedynamically using the python DLL from a python installation) and before trying the fallback registry entries, also the python DLL location should be tried (when existing and different), because that is a stronger indicator for a specific python installation, which may in some cases be different from the registry entry.c)
In Python 2 when the pythonNN.dll was in system32 for a system install, it was enough to put a link/copy of the only python.exe to e.g. C:\bin\python(NN).exe on the PATH (not for the python DLL) for handy short cut. Of course there are several good reasons to not use the system32 folder. But to regain that function,
python.execould become worth its salt and dynamically locate the python DLL (relative & symlink aware + from registry) regardingPy_Main, similar to Pythonwin.exe, instead of having a hard dependency at Windows link level on pythonNN.dll .d)
The symlink / real realpath should be used on Windows as well (at priority before registry entry) regarding detection of the most relevant Python Home. (Of course respecting a nearby pyvenv.cfg before dereferencing the symlink)
skochinsky commentedon Jan 6, 2023
We're also running into this issue. Has anyone figured out a good workaround? Also, how come
python.exeworks correctly?kxrob commentedon Jan 6, 2023
That also works only as far as it sits in the 'home' directory besides
DLLs/andLib/, see.. and explanations from eryksun and in the linked document.
Its obviously a bug which broke "Otherwise, the core Python path is constructed from the PythonPath stored in the registry." (
a)) besides general weaknesses of the implemented logic, which may be improved byb) c) d).Maybe @zooba @vstinner have an idea what caused the bug?
Like mmomtchev (linked issue/commit) I use 3.11.0 until the bug is hopefully fixed again in 3.11.2 . More are facing this bug.
Another workaround would also be possible, but I don't wont to raise memes which trigger adaptation to a bug in the middle of the road.
skochinsky commentedon Jan 6, 2023
I came up with the following workaround for the time being:
skochinsky commentedon Jan 6, 2023
By the way, Anaconda fork has a similar issue (ignoring
PythonPathin registry) which prevents it from being used as embedded runtime:ContinuumIO/anaconda-issues#11374
eryksun commentedon Jan 6, 2023
Part of the change in 3.11.1 is that it no longer searches up from the executable directory to find
exec_prefix, i.e. the directory that contains "DLLs". This change restores compatibility with 3.10 and earlier versions. Searching up would help with the Pythonwin case, but I agree that this is not a secure practice on Windows (e.g. with the default security, any authenticated user can create a directory in the root directory of a volume, including the system volume), and it was a mistake to enable this behavior in 3.11.0. The problem is better addressed in terms of the other changes in 3.11.1.In 3.10 on Windows,
exec_prefixandprefixare always the same, and "{exec_prefix}\DLLs" is added tosys.pathwhether or not it exists. In 3.11.1,exec_prefixis set to the executable directory andplatstdlib_diris set to "{exec_prefix}\DLLs" only if "DLLs" is found in the executable directory. Otherwise bothexec_prefixandplatstdlib_dirare set to the same directory asprefix.I don't understand the cited reason for not setting
platstdlib_dirto "{exec_prefix}\DLLs", as was always done in previous versions. The comment says that this "would give site-packages precedence overexecutable_dir". This might be related to another bug in Modules/getpath.py, in the following code:On Windows,
executable_dirshould always be added, and the order should always beplatstdlib_dir,stdlib_dir,executable_dir. I verified this in versions 3.6-3.10, running with-S(nositeimport) for both the base environment and a virtual environment.I think the above snippet should be modified to remove the
venv_prefixcheck and always addexecutable_dir, which should never be the same asstdlib_dirorplatstdlib_dir(see further modifications below). For example:I'd modify the code that sets
exec_prefixas follows:zooba commentedon Jan 11, 2023
There's a lot of discussion here, but fundamentally it seems like the bug is the registry code not loading properly.
For the (IMHO, broken) scenario where you have a normal Python install but are trying to use it from a different executable, the registry key is how the stdlib should be found. The better layout is to not just copy the Python executable, but also the libraries that match it.1
We have preferred "adjacent to the executable" since 3.6-ish because it is necessary to be able to run a full copy of Python independent from a "real" install. Before that, you could have had a whole copy of 3.5.5 in one directory and an installed copy of 3.5.1 would break it because the registry key "won".
But we shouldn't be breaking the registry here. I'll take a look. I've not been convinced of any of the other changes that are mentioned.
Footnotes
Though it does seem like this is the safest possible approach that involves loading the DLL and initialising it. Could be better if the DLL was initialised properly and provided the paths it needs, but I know that's incredibly painful because... well... it's not how we want 3rd party apps using the Python runtime. Just bring your own copy of it, and don't try and do anything complicated with someone else's install, or else ask the user for the executable path and run it that way. ↩
zooba commentedon Jan 11, 2023
Looks like I missed the case of reading directly from the
PythonPathkey, but got the read for all subkeys. The read of the top one only happens whenhomeisn't known, which is how it slipped through all other validation, because there are very few scenarios where that occurs anymore.Setting
PYTHONHOMEenvironment variable before loading the DLL ought to be a suitable workaround for 3.11.1 if needed (and setting that against 3.10 should also skip reading the registry). But I think we'll be okay with a fix for 3.11.2 to read from that key under these circumstances.5 remaining items
zooba commentedon Jan 11, 2023
In any case, the change I just pushed should satisfy things. The only thing we're discussing now is whether
DLLsshould precedeLibin the case whereDLLsis assumed to be the same asprefixbecause we couldn't find it. I think it should precedeLib, rather than switching back and forth, as I consider "location where our standard .pyd's live" to be the main reason we are including the directory at all.executable_dirbeing in there is considered a mistake that we can't fix, but it can at least remain at the end of the list when that's the only reason we have to include it.eryksun commentedon Jan 11, 2023
The code in 3.10 is partly based on the
PYTHONPATHmacro, which used to be defined asL".\\DLLs;.\\lib"in PC/pyconfig.h. It proceeds as follows:cpython/PC/getpathp.c
Lines 877 to 910 in a3b6577
Thus it adds
(f"{home}\\DLLs", f"{home}\\lib", f"{argv0_path}").In a 3.10 virtual environment,
argv0_pathgoes through a couple steps if the venv launcher is used. First it changes to the value of the environment variable__PYVENV_LAUNCHER__in order to find "pyvenv.cfg". Thenargv0_pathis set to the "home" value in the venv configuration, which should be the base executable directory (venv gets the "home" value from splittingsys._base_executable).In 3.10,
sys.exec_prefixis always the same assys.prefix(i.e.home). So the "DLLs" directory would always be found relative toprefix, if it's found at all. You changed it in 3.11 to allow using the executable directory forsys.exec_prefixif the "DLLs" landmark is found. I'm okay with that change. In 3.11.1, you modified it to no longer search the parent directories for the "DLLs" landmark, relative to the executable directory. I think it was the right decision. The search up strategy is not secure on Windows, both in terms of file security and layout.skochinsky commentedon Jan 12, 2023
Are there tests in CPython test suite which test embedded scenarios? If so, they should be extended with testing native module imports (like ctypes). It seems things got broken multiple times in the past because "
python.exeworks fine so it's all good".zooba commentedon Jan 12, 2023
Yes, but we don't test this scenario because it's not a supported one.
If you want to embed Python, embed all of it, or else configure the paths properly when you initialise it. Taking the executable and hoping it will find an existing install isn't the way.
skochinsky commentedon Jan 12, 2023
assuming we want to rely on an existing full install, is there a howto/example of how to "configure the paths properly" which would not have been broken by this issue?
zooba commentedon Jan 12, 2023
The example at https://docs.python.org/3/c-api/init_config.html#initialization-with-pyconfig basically shows it, and the rest of that page covers things in more detail.
For a full install though, your entry point is
python.exe, notpythonXY.dll. Directly using the DLL from an install you're not running inside of isn't a supported path - it's totally supported for you to embed the DLL into your own app, but not to "borrow" it from an install.So the other way that wouldn't have been broken is to run
python.exeout of the install, rather than your own executable. If there's something you need to do as part of initialization that requires you to use your own executable, then you most likely should be embedding a full copy of Python. (For comparison, Blender embeds a full copy of Python, because they need to do this, while OBS Studio merely adds search paths for their own modules and so can run through a regularpython.exe.)zooba commentedon Jan 12, 2023
Incidentally, I'd love to hear about these if you have them, because I haven't come across anything before that really needed it (without also needing to control the rest of the Python runtime, i.e. bring their own version). Not knowing makes it hard to factor in the scenarios when we're looking at what can/should/might change.
gh-100320: Fix path calculations on Windows when python.exe is moved …
pythongh-100320: Fix path calculations on Windows when python.exe is …
pythongh-100320: Fix path calculations on Windows when python.exe is …
zooba commentedon Jan 16, 2023
Taking the fix as it stands, so we've at least got something.
gh-100320: Fix path calculations on Windows when python.exe is moved …
aundro commentedon Jan 17, 2023
Igor spotted this issue in the context of IDAPython.
By design, IDAPython does not use
python.exeat all, but rather loads the installed Python DLL/so/dylib, precisely so that the user can use whatever method they want to control the packages that are installed on their system (including distribution packages wherever supported.)xyzzy42 commentedon Sep 18, 2024
I do this in my app as well. I don't see how I could possibly use the python executable, as I need the interpreter running inside the same address space as the rest of the code so I can have shared access to the same data.
Using an existing python library means the user can install Python and their packages any numbers of ways, apt, dnf, pip, msys2, anaconda, homebrew, etc.
Why would I want to get into the cross platform Python packaging and installation business?! That's crazy. And it's already been done.