I encountered an issue where shutil.copyfile corrupts the copied file when executed under pythonw.exe on Windows 10 Home. However, running the same operation with python.exe works correctly.
Issue Description
Problem: When using shutil.copyfile in a .pyw script executed with pythonw.exe, the copied file becomes corrupted.
Observation: Running the same .pyw script with python.exe results in a correctly copied file.
Workaround
Solution: Execute the shutil.copyfile operation using python.exe instead of pythonw.exe.
For demonstration, this script creates a temporary directory named modifield_python and copies the current Python.exe executable, renaming to modified_python.exe.
Testing:
Upon launching the by double clicking C:\Users\%USERNAME%\AppData\Local\Temp\modifield_python\modified_python.exe, you will sense that something is not right already: it does not open. The size of the copied executable seems reasonable, but it just doesn’t work anymore.
This ends up at a single call to _Py_read from what I can tell, for which the docstring only states that EOF can be assumed when the return value is 0.
On success, return the number of read bytes, it can be lower than count.
If the current file offset is at or past the end of file, no bytes are read,
and read() returns zero.
So yes, from what I can tell it’s invalid to assume that readinto returning less than length bytes means we are at an EOF since C doesn’t guarantee that behavior for the builtin read, and python doesn’t do anything to guarantee that behavior instead.
Well, it seems like the mystery is solved. I’ve compared against pythonw, and yeah, they are identical. This is overlook on my part as I’ve overfocused on the metadata, not realizing that I’m using statement sys.executable which becomes either pythonw.exe or python.exe and they are different in size.
Note that executing “modified_python.exe”, as copied by your example, requires Python’s installation directory to be in the PATH environment variable, in order to find the dependent DLLs “python312.dll” and “vcruntime140.dll”. Normally this is not required because they’re located in the directory that contains the executable – “python.exe” or “pythonw.exe”, which the system searches implicitly. To avoid the extra PATH requirement, you’ll have to symlink, hardlink, or copy those two DLLs to the temp directory that contains “modified_python.exe”.
usage: windows_create_renamed_copy_of_python_executable.py [-h] file_path
Produce renamed Python Executable on Windows Operating System.
positional arguments:
file_path Path to the new executable file
options:
-h, --help show this help message and exit
Example usage: python windows_create_renamed_copy_of_python_executable.py ".\test.exe"