chriskiehl / Gooey

Turn (almost) any Python command line program into a full GUI application with one line
MIT License
20.5k stars 1.01k forks source link

Script name gets mangled when installed as a console script on Windows #907

Open jenskutilek opened 3 months ago

jenskutilek commented 3 months ago

I have some command line scripts that I added Gooey to, which are installed via an entry in setup.cfg like this:

[options.entry_points]
console_scripts =
    gui_error_demo = gooeyWinBug:main

When installing the module with pip install ., the script gets installed into C:\Users\jens\AppData\Local\Programs\Python\Python311\Scripts\gui_error_demo.exe.

Double-clicking this exe shows the Gooey GUI as expected, but when I click "Start" to actually run the script, I get this error:

C:\Users\jens\AppData\Local\Programs\Python\Python311\python.exe: can't open file 'C:\\Users\\jens\\AppData\\Local\\Programs\\Python\\Python311\\Scripts\\gui_error_demo': [Errno 2] No such file or directory

It seems that the .exe suffix is stripped from the script name (sys.argv[0]), and so when the script is about to be called again by Gooey, it is not found.

Am I doing anything wrong? Is Gooey not meant to be used this way?

(It works when the same module is installed on macOS, where the installed script doesn't get a suffix added in the first place)

A demo project which installs the script gui_error_demo.exe that shows the problem:

gooey_win_bug.zip

Install with pip install . in the unzipped folder.

taifu commented 2 months ago

Same error here: did you fixed it somehow? Ty.

arthursw commented 3 weeks ago

I have the same problem and found a workaround.

Since the problem occurs only on Windows when called from the command line shortcut (commandName or commandName.exe); and not from the python file (python path/to/commandName.py works fine), I call the actual python script when I am in this case.

At the beginning of commandName.py:

import sys, platform, subprocess

if platform.system() == 'Windows' and not sys.argv[0].endswith('.py'):
        subprocess.call([sys.executable, __file__])
        sys.exit()

# [...] rest of the script

sys.argv[0] is the name of the calling command, it should end with .py otherwise the command python __file__ is run. __file__ is the current file path (of commandName.py) and sys.executable is the python executable.

In my case I only open the gui when there are no arguments (when len(sys.argv) == 1) so I do not need to pass them to the subprocess, but it should be possible with subprocess.call([sys.executable, __file__] + sys.argv[1:]).

arthursw commented 3 weeks ago

This workaround might not work for a PyInstaller or cs_Freeze app, since sys.executable will not point to a python interpreter. Well actually I just test it, I don't have the problem with PyInstaller (and probably neither cs_Freeze) since it does not make use of the console script!

taifu commented 3 weeks ago

My workaround is ugly, I call this function in my main:

def check_windows_exe(app_name):
    import platform
    if platform.system() == "Windows":
        to_file = os.path.join(__file__, app_name)
        from_file = to_file + ".exe"
        if os.path.exists(from_file) and not os.path.exists(to_file):
            import shutil
            shutil.copyfile(from_file, to_file)