Search code examples
pythonshebang

interchangeable shebang line in Python script for dual OSes?


A script is developed both on OS X and Windows using a virtualenv. The so-called developer has already installed all required packages using a requirements.txt file, but one problem remains:

If the script is running on OS X, the beginning of each Python file must start like this:

#!/Users/os-x-username/.virtualenvs/some_env/bin/python
#!C:\Users\windows-username\Envs\some_env\Scripts\python.exe

But if developing on Windows, the line order must be switched:

#!C:\Users\windows-username\Envs\some_env\Scripts\python.exe
#!/Users/os-x-username/.virtualenvs/some_env/bin/python

How can the so-called developer avoid this tediousness?


Solution

  • If you don't mind adding extra steps, ou can create a launcher script launcher.py like:

    #!/usr/bin/env python
    
    import subprocess
    import sys
    
    if __name__ != "__main__":
        print("This is a launcher. Please run only as a main script.")
        exit(-1)
    
    INTERPRETERS = {
        "win": r"C:\Users\windows-username\Envs\some_env\Scripts\python.exe",  # Windows
        "darwin": "/Users/os-x-username/.virtualenvs/some_env/bin/python",  # OSX
        "linux": "/home/linux-user/.virtualenvs/some_env/bin/python"  # Linux
        # etc.
    }
    
    TARGET_SCRIPT = "original_script_name.py"
    
    interpreter = None
    for i in INTERPRETERS:  # let's find a suitable interpreter for the current platform
        if sys.platform.startswith(i):
            interpreter = i
            break
    if not interpreter:
        print("No suitable interpreter found for platform:", sys.platform)
        exit(-1)
    
    main_proc = subprocess.Popen([interpreter, TARGET_SCRIPT] + sys.argv[1:])  # call virtualenv
    main_proc.communicate()  # wait for it to finish
    
    exit(main_proc.poll())  # redirect the return code
    

    Since this script is there only to run the original_script_name.py in the desired interpreter for the current platform, it doesn't matter what its shebang is - as long as it picks any Python interpreter it will be fine.

    It would act as a drop-in replacement for your original script (original_script_name.py) so just call launcher.py instead and it will even redirect the CLI arguments if needed.