Search code examples
pythongitcx-freeze

Display git commit hash in frozen Python script (Cx_Freeze)


I'm having a python project and use git as version control software. The software will be deployed using Cx_Freeze.

I would like to display the version and author (and possibly other metadata) captured during the build process (freezing the script) within an About dialogue in my Application.

This is an example of the setup script:

import sys
import subprocess

from cx_Freeze import setup, Executable

build_exe_options = {}
base = "Win32GUI"
version = subprocess.run(['git', 'describe', '--abbrev=4', '--dirty', '--always', '--tags'],
                         capture_output=True, encoding='utf-8').stdout.strip()

setup(
    name="XY",
    version=version,
    author="My Name",
    description="Mysterious GUI application",
    options={"build_exe": build_exe_options},
    executables=[Executable("XY.py", base=base)],
)

Simple example of an About dialogue:

from tkinter import messagebox

def on_about():
    messagebox.showinfo(f'About', 'Software XY, written by {author}, version {version}')
    # Should display e.g. 'Software XY, written by My Name, version 4b06-dirty'

Does anyone know if this is possible and how to achieve this? Thanks to all in advance!


Solution

  • I came up with a first solution where I create a sub-module within the main package of my application when the setup script is being executed. I import the __version__ variable into the __init__.py of that package only when its frozen and if the sub-module exists:

    setup.py:

    import subprocess
    import os.path
    
    import mymodule
    
    from cx_Freeze import setup, Executable
    
    
    def create_versionmodule(packagepath: str):
        """
        creates a file packagepath/_version.py which defines a __version__ variable
        which contains the stripped output of "git describe --dirty --always --tags"
        """
        
        version = subprocess.run(['git', 'describe', '--dirty', '--always', '--tags'],
                                 capture_output=True, encoding='utf-8').stdout.strip()
        with open(os.path.join(packagepath, '_version.py'), mode='w', encoding='utf-8') as file:
            file.write(f'__version__: str = {version!r}\n')
    
    
    build_exe_options = {}
    base = "Win32GUI"
    
    create_versionmodule(packagepath=os.path.dirname(mymodule.__file__))
    
    setup(
        name="XY",
        description="Mysterious GUI application",
        options={"build_exe": build_exe_options},
        executables=[Executable("XY.py", base=base)],
    )
    

    mymodule/__init__.py:

    import sys as _sys
    
    __version__ = 'UNKNOWN'
    if getattr(_sys, "frozen", False):
        try:
            from mymodule._version import __version__
        except ModuleNotFoundError:
            pass
    
    

    Now I can access the version variable from everywhere in my code:

    import mymodule
    
    from tkinter import messagebox
    
    def on_about():
        messagebox.showinfo(f'About', 'Software XY, version {mymodule.__version__}')