Vytváření exe souborů

Přestože na každém dobrém stroji je Python nainstalovaný, a pokud není, tak je často lepší ho tam nainstalovat a volat skripty zcela průhledně, tak přesto někdy může být potřeba nějaký ten exe vytvořit.

Důvody vytváření exe mohou být různé:

Je doporučeno šířit scripty a ne exe.

py2exe

Je ke stažení na adrese http://www.py2exe.org. Podporuje wxPython, Tkinter, PyGTK, pygame i win32com. Modul se nainstaluje standardně do adresáře site-packages. Pro Win98/Me musíte navíc ještě stáhnout Microsoft Layer for Unicode on Windows 95/98/ME Systems (unicows.dll). Je to pěkně popsáno v dokumentaci.

Jednoduchý příklad

Nejprve si vytvořte "pomocný" soubor setup.py ve stejném adresáři, jako je váš script:

# setup.py
from distutils.core import setup
import py2exe

setup(console=["myscript.py"])
# setup(windows=["myscript.py"])  # pro okenní aplikace, aby nevyskakovalo černé okno

Z windowsovské Příkazové řádky zavolejte: python setup.py py2exe. Aby vám tento příkaz neskončil chybou dříve než začal, je třeba dodržet následující pravidla:

  • musíte stát ve složce s programem - tzn., že když dáte dir uvidíte kromě jiného i setup.py i váš program
  • v systémové proměnné PATH musí být cesta k Pythonu, vyzkoušejte tak, že zadejte příkaz python a pokud se python spustí, je vše OK, pokud ne, přidejte Pythona do PATH

Po úspěšné dokončení vám vznikne, kromě jiného, adresář dist, ve kterém bude uloženo vše, co musíte uživateli poslat. Mezi jinými je tam i ten exe soubor. Je zde mnoho dalších voleb (jeden velký exe, okenní programy, NT služby, com servery). Popis najdete na domovské stránce http://www.py2exe.org.

Důležité FAQ pro puntičkáře: Pokud přejmenujete název exe souboru, program přestane chodit! Řada lidí to přejmenovává a pak se nestačí divit a považuje to za chybu programátora. Je třeba do manuálu jasně napsat, že název exe se nesmí přejmenovat jinak program nefachá!

Složitější příklad

from distutils.core import setup
import py2exe

setup(
    name = '....',
    version = '1.0',
    description = '....',
    author = '...',
    author_email = '....',
    url = 'http://...',
    packages = ['....', '....'],
    windows = [{'script': '........',
                 "icon_resources": [(1, ".........")]}],
    options = {'py2exe': {
                      'packages':'...',
                      'includes': '..., ...., ..., ....',
                      'excludes': '..., ... ,... ',
                      "dll_excludes": '..., ,....'}},
    data_files=[('bitmaps', ['....', '....']),
                ('config', ['....']),]
    )

Kromě 'console' a 'windows' existují ještě další klíčová slova a parametry. py2exe je podřízen modulu distutils, který je Pythonu standardně. Takže:

  1. Ve skriptu setup.py můžete využívat všechna klíčová slova a parametry, která obsahuje distutils: http://docs.python.org/dist/simple-example.html.
  2. Navíc můžete použít klíčová slova a parametry, které přidává samotný py2exe. Jejich přehled se v současnosti dá získat pouze takto
import py2exe
help(py2exe)

Další příklad

from distutils.core import setup
import py2exe, sys, os

sys.argv.append('py2exe')

setup(
    options = {'py2exe': {'bundle_files': 1}},
    windows = [{'script': "key.py"}],
    zipfile = None,
)

PyInstaller

Žije na adrese http://pyinstaller.hpcf.upr.edu/ . Je to nástupce již nežijícího Python Installer.

Vypadá jako mohutnější, s daleko více volbami. Pro začátečníky je tedy snad vhodnější py2exe. Podrobný postup je popsán v dokumentaci, takže tady jen stručně.

  1. Není nutno instalovat, rozbalit archiv kamkoliv. Je dobré si k němu ještě pořídit upx: http://upx.sourceforge.net
  2. Konfigurace - spustit soubor Configure.py
  3. vytvoření "mezisouboru" - *.spec - Zde se nadefinují všechny možné volby pro výsledný exe: python Makespec.py '[OPTIONS]' script
  4. sestavení exe - python Build.py specfile, kde místo specfile zadejte jméno souboru *.spec, který vám vznikl z předchozího bodu.

V adresáři distproject vám vznikne, podobně jako u py2exe, vše včetně žádaného exe souboru. Původní script se však dá v exe nalézt v celé své kráse. Další volby je možno najít v dokumentaci, která se stáhne společně s modulem.

cx_Freeze

Funguje pro Linux i Windows: http://starship.python.net/crew/atuining/cx_Freeze/

Autor cx_Freeze prostudoval py2exe, Python Installer a Freeze a některé jejich vlastnosti spojil do výsledného produktu. Hlavní výhodou cx_Freeze je to, že dokáže sestavit spustitelnou binárku na více systémech (Windows, UN*X, Mac) - prostě tam, kde je dostupný Python a překladač a linker C. Na druhou stranu je sestavení spustitelného programu velmi pomalé.

cx_Freeze mám vyzkoušené, dokonce funguje i s velkými moduly PyQt_PySide a cx_Oracle (vlastně by bylo divné, že by to nefungovalo s modulem stejného autora;))

Příklad použití:

FreezePython --include-modules=cx_Oracle --target-dir=oraschemadoc-exe oraschemadoc-dev\oraschemadoc.py
# anebo
FreezePython --include-modules=PyQt4,sip,reportlab,datetime --target=srcdir-bin srcdir/appname.py

User Account Control (UAC)

Potřeba "administrátorských" oprávnění se kóduje do manifestu EXE souboru [CIT1], [CIT2], [CIT3]. V Pythonu lze úpravu provést takto [1]:

import win32api
from xml.etree import ElementTree


RESOURCE_TYPE = 24
RESOURCE_NAME = 1
RESOURCE_LANG = 1033

ASM_V1_NS = "urn:schemas-microsoft-com:asm.v1"
ASM_V3_NS = "urn:schemas-microsoft-com:asm.v3"
ASSEMBLY_TAG = str(ElementTree.QName(ASM_V1_NS, "assembly"))
TRUST_TAG = str(ElementTree.QName(ASM_V3_NS, "trustInfo"))
SECURITY_TAG = str(ElementTree.QName(ASM_V3_NS, "security"))
PRIVILEGES_TAG = str(ElementTree.QName(ASM_V3_NS, "requestedPrivileges"))
EXECUTION_TAG = str(ElementTree.QName(ASM_V3_NS, "requestedExecutionLevel"))

LEVEL_ATTRIBUTE = "level"
UI_ATTRIBUTE = "uiAccess"

LEVEL_VALUES = {None: "asInvoker", False: "highestAvailable", True: "requireAdministrator"}
UI_VALUES = {False: "False", True: "True"}

EXECUTION_PATH = (TRUST_TAG, SECURITY_TAG, PRIVILEGES_TAG, EXECUTION_TAG)

def set_uac(path, level=None, ui_access=False):
    """Set elevation request level of an EXE file.

    If the elevation is not needed, set ``level = None``.
    If the elevation is not necessary, set ``level = False``.
    If the promotion is necessary, set ``level = True``.
    Applications with ``ui_access == True`` must be Authenticode signed to start properly.

    :param path: Path to the EXE file.
    :type path: str
    :param level: Level of elevation request.
    :type level: bool
    :param ui_access: Bypass user interface control levels.
    :type ui_access: bool

    """

    handle = win32api.LoadLibrary(path)
    manifest = win32api.LoadResource(handle, RESOURCE_TYPE, RESOURCE_NAME, RESOURCE_LANG)
    win32api.FreeLibrary(handle)

    root = ElementTree.fromstring(manifest)
    assert root.tag == ASSEMBLY_TAG
    element = get_element(root, EXECUTION_PATH)
    element.set(LEVEL_ATTRIBUTE, LEVEL_VALUES[level])
    element.set(UI_ATTRIBUTE, UI_VALUES[ui_access])
    manifest = ElementTree.tostring(root)

    handle = win32api.BeginUpdateResource(path, 0)
    win32api.UpdateResource(handle, RESOURCE_TYPE, RESOURCE_NAME, manifest, RESOURCE_LANG)
    win32api.EndUpdateResource(handle, 0)

def get_element(root, path):
    """Get the element on given path (create missing elements).

    :param root: Root element.
    :type root: xml.etree.ElementTree.Element
    :param path: List of element names on the path.
    :type path: (str, ...)
    :return: The target element.
    :rtype: xml.etree.ElementTree.Element

    """
    if not path:
        return root
    element = root.find(path[0])
    if element is None:
        element = ElementTree.SubElement(root, path[0])
    return get_element(element, path[1:])
[1]Tento kód funguje dobře s EXE soubory používanými cx_Freeze. Může se stát, že pro jiné soubory budou potřeba jiné konstanty RESOURCE_TYPE, RESOURCE_NAME a RESOURCE_LANG.