Wie aus Python eine einfache exe erstellen (ohne GUI)

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
nfb503
User
Beiträge: 19
Registriert: Donnerstag 5. Juni 2014, 14:26

Hallo,

mein erstes Programm ist nun fertig (so grob). Es handelt sich um einen einfachen Job.

csv Datei einlesen
Daten in Listen speichern
Daten bearbeiten, neu berechnen
Daten wieder als csv speichern

Sicher habe ich noch lange nicht die Kürze von Python ausgereizt aber immoment ist für mich die Lesbarkeit noch etwas wichtiger als der eleganteste Weg.
Das Programm möchte ich euch später vorstellen, damit ihr vielleicht schreibt was ich besser machen sollte.
Zuerst muss ich aber die Verteilung klären und trotz einer langen Nacht mit Tante Google, habe ich nicht gefunden.

Egal, bevor ich abschweife.
Das ganze ist in Python 3, ich benutze die Python IDLE (Python 3.4) und habe Classes sowie ein paar Funktionen in packages ausgelagert.

Im anschluss versucht, mit cxFreeze eine exe zu erstellen, was leider nicht funktioniert.
Die py selber klappt reibungslos, wen ich die exe erstelle, passiert etwas komisches:

- Unter Programme\Test wird eine exe erstellt sowie in dem Verzeichnis selbst (dann unter Build etc).
- Es erscheint eine Fehlermeldung, dass __FILE__ nicht bekannt ist was für mich darauf deutet, dass ein package nicht vorhanden ist (sys?)

Daraus ergeben sich für mich drei Fragen:

- wie soll ich den Code einer py Datei gestalten ? Für mich sollte eine main def vorhanden sein, wichtige Klassen etc ausgelagert, sonst blickt man nicht mehr durch

- wie kann ich eine exe erstellen, die auf Rechnern läuft, auf denen kein Python installiert ist? Erstmal keine GUI nötig, ich will in Ruhe erstmal die Techniken lernen. Aber das Schnüren der Packages scheint doch etwas anspruchsvoll zu sein (aber immerhin lernt man dabei was eigentlich so vor sich geht)

- was mache ich im aktuellen Fall falsch? Unten meine py Datei (das ist nur eine Testdatei, nicht mein Programm) sowie die setup.py, die ich aufgerufen habe (error in Line 7 und Line 11, Name __file__ not is not defined

SETUP:

Code: Alles auswählen

from cx_Freeze import setup, Executable

executables = [
    Executable('test.py')
]

setup(name='test',
      version='0.1',
      description='Sample cx_Freeze script',
      executables=executables
      )
PY Datei:

Code: Alles auswählen

import sys
import os

def main():
	print('Hallo')
	print(str(sys.argv[0]))
	print(str(os.path.dirname(os.path.abspath(__file__))))
	input()
	
if __name__ == '__main__':
	main()
nfb503
User
Beiträge: 19
Registriert: Donnerstag 5. Juni 2014, 14:26

Noch ein kleiner Zusatz:

Das input() habe ich ergänzt, damit das DOS Fenster stehen bleibt und ich den output kontrollieren kann.
BlackJack

Dritter Treffer wenn ich 'py2exe __file__' in eine Suchmaschine tippe: http://www.py2exe.org/index.cgi/WhereAmI
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

nfb503 hat geschrieben:Das input() habe ich ergänzt, damit das DOS Fenster stehen bleibt und ich den output kontrollieren kann.
Das hilft nicht: Wird z.B. eine Exception ausgeloest, wird das Fenster direkt zugehen ohne dir den wichtigen Traceback zu zeigen.

Starte die cmd.exe selbst und darin erst dein Programm, dann geht auch nichts automatisch zu.
nfb503
User
Beiträge: 19
Registriert: Donnerstag 5. Juni 2014, 14:26

Hallo,

erstmal Vielen Dank, py2exe habe ich nich benutzt, da auf der website keine Unterstützng für python3 vermerkt war.
Geht das dann trotzdem ?

Der Test in der py ist deswegen wichtig, da die csv Datei zum Einlesen einfach im gleichen Ordner wie die exe liegen soll.
Daher möchte ich den Pfad ermitteln.

Und danke für den Tipp, die x direkt zu starten.
Es erscheint nun diesselbe Fehlermeldung:

Hallo
test.exe
Traceback (most recent call last):
File "c:\Python34\lib\site-packages\cx_
, in <module>
exec(code, m.__dict__)
File "test.py", line 11, in <module>
File "test.py", line 7, in main
NameError: name '__file__' is not defined
BlackJack

@nfb503: Verdammt da habe ich mich verlesen und den falschen Suchbegriff gewählt. Aber wer hätte das gedacht, wenn man 'cx_Freeze __file__' in eine Suchmaschine tippt bekommt man auch Antworten. Zum Beispiel habe ich gleich als ersten Treffer Python cx_Freeze name __file__ is not defined, und da gleich in der ersten Antwort ein Verweis auf die FAQ von cx_Freeze unter „Problems with running frozen programs”: Using data files.
nfb503
User
Beiträge: 19
Registriert: Donnerstag 5. Juni 2014, 14:26

Hallo BlackJack,

Vielen Dank, die Doku habe ich sogar gelesen, allerdings finde ich mich schwer zurecht, da ich das Prinzip generell nicht richtig in meinem Kopf kriege.
Ich kann kein Beispiel finden wie ich genau die setup.py anpassen muss.

den code der test.py habe ich so geändert.
Jetzt kommt kein Fehler mehr, das Programm läuft aber anscheinend in den If Zweig und es wird nichts ausgegeben.
Die Zeile hier scheint nichts zu bewirken bzw liegt es wahrscheinlich an einem fehlenden Eintrag in der setup.py.

datadir = os.path.dirname(sys.executable)

Kann mir hier jemand nochmal weiterhelfen ?
Ich habe mir auch die samples auf C:\ ale angesehen, da konnte ich sowas in der art aber ich nicht entdecken.


Code: Alles auswählen

import sys
import os

def main():
	print('Hallo')
	if getattr(sys, 'frozen',False):
		datadir = os.path.dirname(sys.executable)
		print(str(datadir))
	else:
		print(str(sys.argv[0]))
		print(str(os.path.dirname(os.path.abspath(__file__))))
	
if __name__ == '__main__':
	main()
BlackJack

@nfb503: Da in beiden Zweigen etwas ausgegeben wird *muss* ja irgendetwas ausgegeben werden.

Was sollen die ganzen `str()`-Aufrufe eigentlich bewirken?
nfb503
User
Beiträge: 19
Registriert: Donnerstag 5. Juni 2014, 14:26

Halt liebes Forum!

Ich glaube, jetzt habe ich es gelöst.
Anscheinend musste noch getattr importiert werden und in der setup.py habe ich auch noch etwas geändert.

Jetzt scheint es zu gehen!!! Ich bin voll happy!!!!
Danke für eure Hilfe und habt Nachsicht.
Vieles kann man googeln aber wenn man noch so wenig weiß, dass man die Lösung einfach nicht erkennt bzw herausliest,
braucht man einen kleinen Schube :)

So sieht es nun aus:

Aufruf der py Datei direkt:
Hallo
Nicht Frozen
M:\Python\py\testparamter\
M:\Python\py\testparamter

Aufruf der exe Datei:
Hallo
Frozen
M:\Python\py\testparamter\build\exe.win32-3.4

Ich denke, damit ist der Beweis erbracht, oder wie seht ihr das ?

Unten noch die neueste py Datei und Setup.py Datei für die nächsten ratlosen User.

Bei mir bleibt allerdings noch die Frage, wie ich packages einbinden kann in die exe Datei, die ich selber erstellt habe.

Py Datei:

Code: Alles auswählen

import sys
import os
from operator import attrgetter

def main():
	print('Hallo')
	if getattr(sys, 'frozen',False):
		print('Frozen')
		datadir = os.path.dirname(sys.executable)
		print(str(datadir))
	else:
		print('Nicht Frozen')
		print(str(sys.argv[0]))
		print(str(os.path.dirname(os.path.abspath(__file__))))
	
if __name__ == '__main__':
	main()
SETUP PY
from cx_Freeze import setup, Executable

executables = [
Executable('test.py')
]

includes = ["os","sys","win32com.shell"]

setup(name='test',
version='0.1',
description='Sample cx_Freeze script',
executables=executables
)
nfb503
User
Beiträge: 19
Registriert: Donnerstag 5. Juni 2014, 14:26

Ach ja Black Jack, die str() Umklamerungen habe ich gemacht da ich mir nicht sicher war ob ich eine Typumwandlung brauche.
Ich wollte nur ausschliessen, dass es an einer Kleinigkeit liegt!
nfb503
User
Beiträge: 19
Registriert: Donnerstag 5. Juni 2014, 14:26

Und nun dachte ich mir, ich lagere dies in eine def aus, die als return das aktuelle Arbeitsverzeichnis liefert.
Dann könnte ich eine zweite Funktion basteln, die zurückgibt, ob es in diesem Verzeichnis die erwartete csv Datei gibt.

Ist das fürs Erste so richtig, also dieses Vorgehen ?

Mir ist es ganz wichtig zu Beginn, von euren Codevorschlägen bzw Design zu lernen.
Bei VBA habe ich lange gebraucht, um sinnlose oder schlechte Techniken wieder loszuwerden, das möchte ich diesmal besser machen.
nfb503
User
Beiträge: 19
Registriert: Donnerstag 5. Juni 2014, 14:26

Die neue py sieht nun so aus:

Code: Alles auswählen

import sys
import os
from operator import attrgetter

def GetDirName():
	if getattr(sys, 'frozen',False):
		print('Frozen')
		datadir = os.path.dirname(sys.executable)
	else:
		print('Nicht Frozen')
		print(str(sys.argv[0]))
		datadir = os.path.dirname(os.path.abspath(__file__))
	return datadir
	
def main():
	print('Hallo')
	ThisDirName = GetDirName()
	print(ThisDirName)
	
if __name__ == '__main__':
	main()
Benutzeravatar
/me
User
Beiträge: 3554
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

nfb503 hat geschrieben:Die neue py sieht nun so aus
Ah, da hat hier offensichtlich bisher noch niemand den Style Guide for Python Code erwähnt.

Ich würde zudem nicht Funktionslogik mit Ausgaben mischen oder anders ausgedrückt: GetDirName sollte keine Aufrufe von print enthalten.
nfb503
User
Beiträge: 19
Registriert: Donnerstag 5. Juni 2014, 14:26

Danke /me!
Print Anweisungen habe ich nun entfernt, war mehr zum Testen vorgesehen ob überhaupt etwas passiert.
Und Vielen Dank für den Link, ist sogleich gespeichert!

Bis auf die print Anweisungen, gibt es andere "No's", die Du mir mitteilen kannst?
Verdammt, gerade mal ein paar Zeilen getippt und schon sowas :lol:
Zum Glück hast du mein eigentliches Programm noch nicht gesehen :wink: aber das kommt ja noch hoffentlich.

Weiterhin:

Wäre es nicht besser, meine Funktion nochmal aufzuteilen gemäß dem Grundsatz pro Funktion nur eine Aufgabe?

Denn immoment macht meine Funktion:

Prüft, ob Frozen true oder false
Legt Arbeitsverzeichnis fest

In dem Rahmen mag das übertrieben wirken, aber ich diskutiere gerne über sowas.
Meine Idee ist:

Eine FUnktion, die prüft ob Frozen YES / NO
Eine Funktion, die (vielleicht mit Parameter) Verzeichnis definiert.

Was denkst du darüber ?
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

nfb503 hat geschrieben:Wäre es nicht besser, meine Funktion nochmal aufzuteilen gemäß dem Grundsatz pro Funktion nur eine Aufgabe?
Ja, das Auslagern des Tests auf "frozen" könnte man auslagern. Ansonsten kann man deinen Code noch vereinfachen:

Code: Alles auswählen

def get_dir_name():
    try:
        sys.frozen
    except AttributeError:
        path = os.path.abspath(__file__)
    else:
        path = sys.executable

    return os.path.dirname(path)
Oder eben mit einer "is_frozen"-Funktion:

Code: Alles auswählen

def get_dir_name():
    path = sys.executable if is_frozen() else os.path.abspath(__file__)

    return os.path.dirname(path)
Das Leben ist wie ein Tennisball.
nfb503
User
Beiträge: 19
Registriert: Donnerstag 5. Juni 2014, 14:26

Vielen Dank, das sieht wunderbar aus.

Die Logik verstehe ich nur nicht.
Zeile sys.frozen wird versucht auszuführen.
Wenn hier ein Fehler auftritt (AttributeError), ist das Sys eigentlich nicht frozen und __file__ kan benutzt werden

Die Technik dahinter verstehe ich nicht ganz und ich würde gerne das "dahinter" auch gerne begreifen
BlackJack

@nfb503: Der Import von `attrgetter` kann weg, das wird doch nirgends verwendet.
nfb503
User
Beiträge: 19
Registriert: Donnerstag 5. Juni 2014, 14:26

Ich dachte, ich brauche es, da ohne weder das If noch das Else ausgeführt wurde für diese Zeile:

getattr(sys, 'frozen',False):

Ich teste es nochmal genau, da mehrere Dinge von mir verändert wurden
Benutzeravatar
/me
User
Beiträge: 3554
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

nfb503 hat geschrieben:Die Logik verstehe ich nur nicht.
Zeile sys.frozen wird versucht auszuführen.
Wenn hier ein Fehler auftritt (AttributeError), ist das Sys eigentlich nicht frozen und __file__ kan benutzt werden
Das steht auch fast so im Code (vom "ausführen" mal abgesehen). Es wird ein Zugriff auf sys.frozen versucht. Existiert das nicht, so kommt es zu einem AttributeError und es wird __file__ verwendet um den Pfad zu bestimmen.

Ich verstehe jetzt das Verständnisproblem nicht.
nfb503
User
Beiträge: 19
Registriert: Donnerstag 5. Juni 2014, 14:26

Also ist .frozen eine Eigenschaft, die nur gesetzt wird, wenn es "eingefroren" ist, richtig?
Mich würde interessieren wie genau dies im Hintergrund abläuft, aber ich sehe erstmal in der Dokumentation nach (Module docs).
Antworten