Python program distribution

Python is a scripting language. Programs need to rely on the Python interpreter when running, and the corresponding dependency libraries must be installed. For Python users like me, it’s not a big deal. A PIP is all that matters. But if you’re distributing to other Windows users, especially those unfamiliar with Python, it’s too much trouble. Therefore, it is best to package the Python interpreter and Python programs together and solve the problem in a single installation through Inno Setup.

Embedded Python processing

This is not hardware embedded, but the official Python installation-free interpreter, you can download the required version from here: Python for Windows.

Be careful to download the embeddable version, otherwise you can’t use it to make installers.

PIP get-pip.py = “PIP”; PIP get-pip.py = “PIP”;

python.exe get-pip.py
Copy the code

The Scripts subdirectory is generated in the root directory to store the exe generated after the installation:

Once PIP is installed, the required dependency libraries and main programs can be installed. In the case of my blog crawler framework, djsc.exe is generated in Scripts (see figure above). The program is now deployed.

However, the current Python integration package cannot be used for distribution, because PIP writes the python interpreter path to the EXE when installing the programs that generate the exe. You need to manually remove the contents of the absolute path and keep python.exe only:

Make a Windows installer

After removing the absolute path to the interpreter, you can create installation packages using Inno Setup. I recommend using the QuickStart Pack for Inno Setup, which installs Inno Script Studio automatically and has a great experience. Here is my script file (djscf.iss) :

; Script generated by the Inno Script Studio Wizard. ; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES! #define MyAppName "DJSCF" #define MyAppVersion "0.0.3.2" #define MyAppPublisher "Hochikong" #define MyAppURL "https://github.com/Hochikong/DoujinshiCollectorFramework" #define MyAppExeName "Scripts\djsc.exe" [Setup] ; NOTE: The value of AppId uniquely identifies this application. ; Do not use the same AppId value in installers for other applications. ; (To generate a new GUID, click Tools | Generate GUID inside the IDE.) AppId={{53AC6536-1651-4A86-AB6B-69583B96FA78} AppName={#MyAppName} AppVersion={#MyAppVersion} ; AppVerName={#MyAppName} {#MyAppVersion} AppPublisher={#MyAppPublisher} AppPublisherURL={#MyAppURL} AppSupportURL={#MyAppURL} AppUpdatesURL={#MyAppURL} DefaultDirName={pf}\{#MyAppName} DefaultGroupName={#MyAppName} AllowNoIcons=yes LicenseFile=C:\Users\ckhoi\Desktop\DJSCFramework\LICENSE.txt InfoAfterFile=C:\Users\ckhoi\Desktop\DJSCFramework\README.txt OutputDir=C:\Users\ckhoi\Desktop OutputBaseFilename=djscForWindows-v0.0.3.2-setup Compression= lzMA SolidCompression=yes [Code] function NeedsAddPath(Param: string): boolean; var OrigPath: string; begin if not RegQueryStringValue(HKEY_LOCAL_MACHINE, 'SYSTEM\CurrentControlSet\Control\Session Manager\Environment', 'Path', OrigPath) then begin Result := True; exit; end; { look for the path with leading and trailing semicolon } { Pos() returns 0 if not found } Result := Pos('; ' + Param + '; ', '; ' + OrigPath + '; ') = 0; end; [Languages] Name: "english"; MessagesFile: "compiler:Default.isl" Name: "chinesesimplified"; MessagesFile: "compiler:Languages\ChineseSimplified.isl" [Tasks] Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked Name: "quicklaunchicon"; Description: "{cm:CreateQuickLaunchIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked; OnlyBelowVersion: 0,6.1 [Files] Source: "C:\Users\ckhoi\Desktop\DJSCFramework\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs ; NOTE: Don't use "Flags: ignoreversion" on any shared system files [Icons] Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}" Name: "{group}\{cm:ProgramOnTheWeb,{#MyAppName}}"; Filename: "{#MyAppURL}" Name: "{group}\{cm:UninstallProgram,{#MyAppName}}"; Filename: "{uninstallexe}" Name: "{group}\ Open configuration file "; Filename: "{app}\Scripts\config.ini" Name: "{commondesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon Name: "{userappdata}\Microsoft\Internet Explorer\Quick Launch\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: quicklaunchicon [Setup] AlwaysRestart = yes [Run] Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent [Registry] Root: "HKLM"; Subkey: "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"; ValueType: expandsz; ValueName: "Path"; ValueData: "{olddata}; {app}"; Check: NeedsAddPath('{app}')Copy the code

The most important points are as follows:

  • #define MyAppExeName "Scripts\djsc.exe"
    Copy the code

    When creating a script using ISS’s wizard, you will be asked to fill in the path of the main program, as shown below:

    However, our exe is in a subdirectory of the embedded Python directory Scripts. If we set the main program to djsc.exe directly in the wizard without modifying the above script, Djsc.exe is extracted from the Scripts directory into the root directory after installation and generates a start menu shortcut for it.

    The best way to solve this problem is to modify MyAppExeName directly, preceded by subdirectories.

  • [Code]
    function NeedsAddPath(Param: string): boolean;
    var
      OrigPath: string;
    begin
      if not RegQueryStringValue(HKEY_LOCAL_MACHINE,
        'SYSTEM\CurrentControlSet\Control\Session Manager\Environment'.'Path', OrigPath)
      then begin
        Result := True;
        exit;
      end;
      { look for the path with leading and trailing semicolon }
      { Pos() returns 0 if not found }
      Result := Pos('; ' + Param + '; '.'; ' + OrigPath + '; ') = 0;
    end; [Registry] Root: "HKLM"; Subkey: "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"; ValueType: expandsz; ValueName: "Path"; ValueData: "{olddata};{app}"; Check: NeedsAddPath('{app}')
    Copy the code

    The above code is used to write the installation PATH in the PATH when installing, the installation PATH is ‘{app}’. You can call Python directly from CMD by writing the installation Path to Path. However, users can not use it directly after installation, but also need the following code.

  • [Setup]
    AlwaysRestart = yes
    Copy the code

    The above code allows the user to choose whether or not to restart the computer, and djsc.exe can be used normally after the restart.

  • Name:"{group}\ Open configuration file "; Filename: "{app}\Scripts\config.ini"
    Copy the code

    This code adds a shortcut to the configuration file to the start menu, preventing the user from manually opening the directory for editing.

conclusion

By doing this, you can package Python programs with interpreters and dependent libraries and distribute them to non-specialist users, especially PyQt programs. Pyinstaller often fails to package if a dependent library uses a library such as Numpy, but packaging it as an installation package improves the user experience. This method adds python.exe to the PATH. If you need to install another Python, you may need to change the interpreter of the generated exe to a different name, such as pyi.exe. Then create a symlink to python.exe or simply change python.exe to pyi.exe so that the user’s own interpreter is not affected.