Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 14 additions & 3 deletions build_scripts/windows/scripts/build.cmd
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,21 @@ if not exist %PYTHON_DIR% (
del python-archive.zip
echo Python downloaded and extracted successfully

REM Delete _pth file so that Lib\site-packages is included in sys.path
REM Append `import site` to python*._pth so site.py runs at startup
REM (which adds Lib\site-packages to sys.path). We keep the file (rather
REM than deleting it) because on Python 3.14+ removing it breaks pip's
REM PEP 517 isolated BuildEnvironment subprocess: the child python.exe
REM can no longer locate the stdlib zip and dies in init_fs_encoding
REM with ModuleNotFoundError: No module named 'encodings'.
Comment on lines +115 to +118
Copy link
Copy Markdown
Member

@jiasli jiasli May 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you find evidence that Python 3.14 specifically changed the behavior when _pth file is deleted?

The del python*._pth logic was there since long ago as a manual process and was added to build.cmd in #21746 (comment).

Copy link
Copy Markdown
Contributor Author

@YangAn-microsoft YangAn-microsoft May 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No there isn't a specific change introduced by Python 3.14 that caused the issue. In fact, only Python 3.14 win32 embedded has trouble importing from python*.zip.
I came up with this fix after experimenting the official way of enabling site packages for embedded python. (https://docs.python.org/3/using/windows.html#finding-modules)
Since this solves the win32 issue and using a more officially recommended solution than existing del python*._pth, I believe it's a proper change.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR is correct. import site is indeed the documented way of importing site packages.

Actually, python314._pth contains

python314.zip
.

# Uncomment to run site.main() automatically
#import site

However, there may be deeper reason why encodings cannot be imported.

only Python 3.14 win32 embedded has trouble importing from python*.zip

In my testing, with or without python314._pth in the embedded Python folder, both amd64 and win32 Python can import encodings correctly:

PS D:\temp\python-3.14.5-embed-amd64> ./python.exe -c "import encodings; print(encodings)"
<module 'encodings' from 'D:\\temp\\python-3.14.5-embed-amd64\\python314.zip\\encodings\\__init__.pyc'>

PS D:\temp\python-3.14.5-embed-win32> ./python.exe -c "import encodings; print(encodings)"
<module 'encodings' from 'D:\\temp\\python-3.14.5-embed-win32\\python314.zip\\encodings\\__init__.pyc'>

It may be related to pip's isolated BuildEnvironment.

REM Keeping an explicit _pth makes stdlib + site-packages discoverable
REM in both the parent and any spawned subprocess.
REM https://github.com/pypa/pip/issues/4207#issuecomment-297396913
REM https://docs.python.org/3.10/using/windows.html#finding-modules
del python*._pth
REM https://docs.python.org/3/using/windows.html#finding-modules
if exist python*._pth (
for %%f in (python*._pth) do (
findstr /x "import site" "%%f" >nul || echo import site>> %%f
)
)
Comment on lines +123 to +127
Copy link
Copy Markdown
Member

@jiasli jiasli May 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is nice to have a more general and robust logic. It's ok to keep your current logic.

Nevertheless, the build script only downloads from a fixed URL

set PYTHON_DOWNLOAD_URL="https://www.python.org/ftp/python/%PYTHON_VERSION%/python-%PYTHON_VERSION%-embed-%PYTHON_ARCH%.zip"

Simply appending import site should be sufficient.

Moreover, the glob may introduce an unnecessary attack surface. The glob python*._pth in the for loop would match any file fitting that pattern in the directory — if an unexpected or malicious file exists there, it would also get import site appended to it, though that would very unlikely happen.


echo Installing pip
curl --output get-pip.py %GET_PIP_DOWNLOAD_URL%
Expand Down
Loading