'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 1:[1]

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__}')

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 slarag