Pfadprobleme beim Aufruf externer Programme

Python in C/C++ embedden, C-Module, ctypes, Cython, SWIG, SIP etc sind hier richtig.
Antworten
Benutzeravatar
woody55
User
Beiträge: 13
Registriert: Freitag 4. Mai 2012, 22:00
Wohnort: /welt/graz/home

Hallo, liebe Forenteilnehmer!

Ich habe folgendes Problem:
Ein Programm erzeugt mir eine Latex-Quelldatei, hier "daxbuysellsma20.tex".

Code: Alles auswählen

  
reportfile = "daxbuysellsma20"
make_report(reportfile + ".tex")
Danach möchte ich mit subprocess.call Latex starten und diese Datei in eine DVI-Datei konvertieren lassen.
Anschließend soll noch "dvipdf" daraus ein PDF erstellen.

Code: Alles auswählen

subp.call("latex " + reportfile + ".tex", shell=True)
subp.call("dvipdf " + reportfile + ".dvi", shell=True)
Ok, das geht nicht, weil die subprocess-Shell einen anderen $PATH

Code: Alles auswählen

/usr/bin:/bin:/usr/sbin:/sbin
/bin/sh: latex: command not found
als mein System-Environment hat:

Code: Alles auswählen

echo $PATH
/Library/Frameworks/Python.framework/Versions/3.2/bin:/Library/Frameworks/Python.framework/Versions/2.7/bin:/opt/local/bin:/opt/local/sbin:/opt/local/bin:/opt/local/sbin:/opt/local/bin:/opt/local/sbin:/opt/local/bin:/opt/local/sbin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/usr/X11/bin:/usr/texbin
Latex und dvipdf funktionieren in der Konsole einwandfrei.

Dann setzen wir halt die absoluten Pfade ein:

Code: Alles auswählen

    
subp.call("/usr/texbin/latex " + reportfile + ".tex", shell=True)
subp.call("/opt/local/bin/dvipdf " + reportfile + ".dvi", shell=True)
Und dann haut es "dvipdf" auf, weil es "dvips" und "ghostscript" aufruft.

Code: Alles auswählen

/opt/local/bin/dvipdf: line 47: exec: dvips: not found
/opt/local/bin/dvipdf: line 47: gs: command not found
Für mich gibt es hier zwei Möglichkeiten, von denen ich keine mangels tieferem Wissen realisieren kann:
(1) Ich teile der subprocess-Shell meinen bash-Pfad mit
(2) Ich rufe eine Shell auf, die meinen System-Environment-Pfad übernimmt

Oder gibt es Möglichkeit (3)?
Das Kummerobjekt: Mac OSX Lion/Python 2.7.3
Oder ist es eine Mac-spezifische Macke?

Bitte um Hilfe!
Nach Diktat vereist.
Äh - nein, falsch.
Nach Diktat vergreist.
Mist!
Nach Dicktat ... es interessiert sowieso niemanden.
lunar

@woody: Bitte verwende nicht "shell=True". In Deinem Beispiel ist das vollkommen überflüssig. Erzeuge stattdessen eine Liste mit Argumenten, dann funktioniert Dein Beispiel auch problemlos, wenn man Leerzeichen im Dateinamen vorkommen.

Betreffs des eigentlichen Problems: Lass Dir mal im Python-Skript den Inhalt von "os.environ['PATH']" ausgeben.
Benutzeravatar
woody55
User
Beiträge: 13
Registriert: Freitag 4. Mai 2012, 22:00
Wohnort: /welt/graz/home

@lunar: Danke für deine schnelle Antwort!
lunar hat geschrieben:@woody: Bitte verwende nicht "shell=True". In Deinem Beispiel ist das vollkommen überflüssig. Erzeuge stattdessen eine Liste mit Argumenten, dann funktioniert Dein Beispiel auch problemlos, wenn man Leerzeichen im Dateinamen vorkommen.
Ich weiss, dass shell=True in Verbindung mit eine eingeschleusten "rm -rf" eine tolle Sache ist :twisted:
Das Argument mit der Liste ist gut erklärt! Super!
Betreffs des eigentlichen Problems: Lass Dir mal im Python-Skript den Inhalt von "os.environ['PATH']" ausgeben.
Das tut auch nicht mehr als die anderen Versuche:

Code: Alles auswählen

subp.call(["/usr/texbin/latex","-v"])    
print os.environ['PATH']
liefert auch nur:

Code: Alles auswählen

/usr/bin:/bin:/usr/sbin:/sbin
Zu denken hat mir der Abschnitt "environ" bei http://docs.python.org/library/os.html#module-os gegeben:
Note On some platforms, including FreeBSD and Mac OS X, setting environ may cause memory leaks. Refer to the system documentation for putenv().
Auch das hier

Code: Alles auswählen

pfad=''
os.putenv('PATH', pfad)
print "os.putenv('PATH', pfad)=>",pfad,"<"
geht nicht

Code: Alles auswählen

os.putenv('PATH', pfad)=>  <
Ich probiers jetzt einmal unter Linux.
Nach Diktat vereist.
Äh - nein, falsch.
Nach Diktat vergreist.
Mist!
Nach Dicktat ... es interessiert sowieso niemanden.
Benutzeravatar
woody55
User
Beiträge: 13
Registriert: Freitag 4. Mai 2012, 22:00
Wohnort: /welt/graz/home

Ok, Linux bringt es an Tag: Es ist eine Mac-spezifische Sch...e! :evil:

Unter Linux generiert folgender Code

Code: Alles auswählen

    subp.call("echo $PATH")
    print os.environ['PATH']
    subp.Popen("echo $PATH")
    subp.Popen("latex -v")
    
    #subp.call(["/usr/texbin/latex","-v"])    
    subp.call(["latex","-v"])    
    subp.call(["dvipdf"])    
    print os.environ['PATH']
    
    pfad=''
    os.putenv('PATH', pfad)
    print "os.putenv('PATH', pfad)=>",pfad,"<"
diesen Output

Code: Alles auswählen

/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games
pdfTeX 3.1415926-1.40.10-2.2 (TeX Live 2009/Debian)
kpathsea version 5.0.0
Copyright 2009 Peter Breitenlohner (eTeX)/Han The Thanh (pdfTeX).
There is NO warranty.  Redistribution of this software is
covered by the terms of both the pdfTeX copyright and
the Lesser GNU General Public License.
For more information about these matters, see the file
named COPYING and the pdfTeX source.
Primary author of pdfTeX: Peter Breitenlohner (eTeX)/Han The Thanh (pdfTeX).
Compiled with libpng 1.2.44; using libpng 1.2.44
Compiled with zlib 1.2.3.4; using zlib 1.2.3.4
Compiled with poppler version 0.16.4

pdfTeX 3.1415926-1.40.10-2.2 (TeX Live 2009/Debian)
kpathsea version 5.0.0
Copyright 2009 Peter Breitenlohner (eTeX)/Han The Thanh (pdfTeX).
There is NO warranty.  Redistribution of this software is
covered by the terms of both the pdfTeX copyright and
the Lesser GNU General Public License.
For more information about these matters, see the file
named COPYING and the pdfTeX source.
Primary author of pdfTeX: Peter Breitenlohner (eTeX)/Han The Thanh (pdfTeX).
Compiled with libpng 1.2.44; using libpng 1.2.44
Compiled with zlib 1.2.3.4; using zlib 1.2.3.4
Compiled with poppler version 0.16.4

/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games
os.putenv('PATH', pfad)=>  <
Usage: dvipdf [options...] input.dvi [output.pdf]
Alle vier Methoden liefern den korrekten $PATH des Systems!
("os.putenv('PATH', pfad)=> <" funktioniert nicht!)

Bestätigt nur wieder einmal meine Meinung, dass MAC OS ein nettes Betriebssystem für Alltagsarbeit und Freizeit ist, aber zum Entwickeln nur bedingt brauchbar ist. Gelobt seien die Entwickler von "Parallels"!
Zuletzt geändert von woody55 am Samstag 7. Juli 2012, 15:56, insgesamt 1-mal geändert.
Nach Diktat vereist.
Äh - nein, falsch.
Nach Diktat vergreist.
Mist!
Nach Dicktat ... es interessiert sowieso niemanden.
deets

woody55 hat geschrieben: Bestätigt nur wieder einmal Meinung, dass MAC OS ein nettes Betriebssystem für Alltagsarbeit und Freizeit ist, aber zum Entwickeln nur bedingt brauchbar ist. Gelobt seien die Entwickler von "Parallels"!
Jep, so is richtig - die eigene Inkompetenz schnell mal auf's System geschoben...

Code: Alles auswählen


import os
import tempfile
import subprocess

path = os.environ["PATH"]
print path

td = tempfile.mkdtemp()

testcmd = os.path.join(td, "testcmd")

with open(testcmd, "wb") as outf:
    outf.write("""#!/bin/bash
echo "hallo"
""")
    os.fchmod(outf.fileno(), 0766)


subprocess.call([testcmd])
try:
    subprocess.call(["testcmd"])
except OSError:
    print "nich im pfad"

os.environ["PATH"] = path + ":" + td
subprocess.call(["testcmd"])
Nur weil du weder deinen Pfad korrekt gesetzt noch den Umgang mit os.environ beherrschst macht OSX noch lange nix falsch...
lunar

@deets: Dieser persönliche Angriff (ja, die anlasslose Unterstellung von Inkompetenz ist ein persönlicher Angriff) ist weder nötig noch hilfreich. Lies die ganze Diskussion: Der OP hat den Pfad korrekt gesetzt, denn laut Aussage in seinem allerersten Beitrag funktioniert der Aufruf der entsprechenden Programme direkt im Terminal.

@woody55: Du kannst den Pfad über "os.environ" verändern. Ich würde allerdings davon absehen, den Pfad prozessweit zu ändern, und ihn stattdessen speziell für die Unterprozesse anpassen:

Code: Alles auswählen

env = dict(os.environ)
env['PATH'] = '/opt/local/bin/{sep}{path}'.format(sep=os.pathsep, path=env['PATH'])
subprocess.call([...], env=env)
Damit erhält der Unterprozess die in "env" enthaltene Umgebung, und vererbt diese ‒ einschließlich $PATH ‒ auch an seine eigenen Unterprozesse.

Anstatt den Pfad im Skript zu kodieren, ist es allerdings sinnvoller, herauszufinden, warum der Pfad schon beim Start des Skripts fehlerhaft ist. Unter OS X hängt die Umgebung eines Prozesses auch davon ab, wie der Prozess gestartet wird. Insofern ist es wichtig zu wissen, welche OS X-Version Du verwendest, und wie genau Du das Skript aufrufst, sprich aus dem Terminal oder von einem anderen Ort aus, wie beispielsweise einen über die graphische Oberfläche gestarteten Editor.
Benutzeravatar
woody55
User
Beiträge: 13
Registriert: Freitag 4. Mai 2012, 22:00
Wohnort: /welt/graz/home

Jep, so is richtig - die eigene Inkompetenz schnell mal auf's System geschoben...
Hallo deets,

danke für dein Beispiel. Den Vorwurf der Inkompetenz akzeptiere ich, da ich erst seit etwas über drei Monaten mit Python arbeite und nicht alles wissen kann. Meine eher schlechte Meinung vom Mac als Entwicklungssystem ist in dieser Form sicher falsch ausgedrückt, meist sind es eher nicht ganz einwandfreie Portierungen, mit denen ich nicht ganz klar komme. Unter Linux läuft es für mich etwas runder.

Aber mittlerweile kann ich die Schuld schon wieder weiterschieben: Es ist Eclipse!
Eclipse unter Mac:

Code: Alles auswählen

pfad = os.environ['PATH']
print pfad
liefert

Code: Alles auswählen

/usr/bin:/bin:/usr/sbin:/sbin
Mit ipython unter Mac erhält man:

Code: Alles auswählen

In [2]: path = os.environ["PATH"]
In [3]: path
Out[3]: '/Library/Frameworks/Python.framework/Versions/3.2/bin:/L....
Das ist also richtig.

Es wäre sehr lieb von dir, wenn du mir erklären könntest, warum das so ist. Nein, ich habe die vielhundertseitige Dokumentation zu Eclipse nicht gelesen.

Ich mag zwar unter Python ein Anfänger sein, aber ich habe 1977 auf der Uni als erste Programmiersprache Fortran gelernt und programmiere damit schon länger, als die meisten Forumsteilnehmer auf der Welt sind und neige nicht dazu, nach einer Viertelstunde fluchend alles hinzuschmeissen.

In deinem Beispiel fügst du den Pfad zu deinem Programm dazu. Das ist durchaus einleuchtend.
Wie ich jedoch die Pfade zu Programmen hinzufügen, soll die von einem anderen Programm aufgerufen werden, ist mir noch immer nicht klar: dvipdf ruft ghostscript und dvips auf. Wie soll ich diesen Pfad ändern?

Liebe Grüße
woody
Nach Diktat vereist.
Äh - nein, falsch.
Nach Diktat vergreist.
Mist!
Nach Dicktat ... es interessiert sowieso niemanden.
deets

lunar hat geschrieben:@deets: Dieser persönliche Angriff (ja, die anlasslose Unterstellung von Inkompetenz ist ein persönlicher Angriff) ist weder nötig noch hilfreich. Lies die ganze Diskussion: Der OP hat den Pfad korrekt gesetzt, denn laut Aussage in seinem allerersten Beitrag funktioniert der Aufruf der entsprechenden Programme direkt im Terminal..
Ich fand seine Auesserungen zum Thema Eignung von OSX zur Softwareentwicklung einen Ausdruck von Inkomptenz. Mag dir nicht Anlass genug sein, ich finde das absondern pauschaler Auesserungen dieser Art sind so zu werten.

Aber zur Sache: der OP hat den pfad offensichlich *NICHT* korrekt gesetzt. Denn sonst wuerden die gestarteten Kommandos ja funktionieren. Tun sie aber nicht. Und in keinem der hier gezeigten Code-Snippets sehe ich, dass er den Pfad *setzt*. Warum auch immer der in der subprocess-Umgebung anders ist als auf der Shell in der er Python startet - das tut mein Python unter OSX nicht, und da kann man jetzt lange rumraten, was da auf seinem System los ist. Das ist aber definitiv kein Indiz fuer die Eignung oder den Mangel daran von OSX zur Programmierung zu taugen.
Benutzeravatar
woody55
User
Beiträge: 13
Registriert: Freitag 4. Mai 2012, 22:00
Wohnort: /welt/graz/home

Hallo lunar,

danke für diesen Tipp, so etwas habe ich gesucht. Aber wie sich jetzt zeigt, ist ein Problem mit Eclipse und nicht von Mac OSX.

Code: Alles auswählen

env = dict(os.environ)
env['PATH'] = '/opt/local/bin/{sep}{path}'.format(sep=os.pathsep, path=env['PATH'])
subprocess.call([...], env=env)
Ich verwende MAC OS 10.7.4.

Liebe Grüße
woody
Nach Diktat vereist.
Äh - nein, falsch.
Nach Diktat vergreist.
Mist!
Nach Dicktat ... es interessiert sowieso niemanden.
deets

@woody55

Warum Eclipse das macht oder nicht - kA. Ich vermute einfach mal, die haben fuer gestartete Interpreter eine definierte Umgebung - und deine Shell wird ja durch .bashrc bzw. .profile konfiguriert - aber das hat mit Eclipse nix zu tun. Eintraege wie /opt/local bekommt es so nicht mit - die musst du explizit dem Interpreter mitgeben, via Run-Configuartion oder so.

Und was ich dir gezeigt habe sollte auf fuer von subprozessen ihrerseits gestartete Prozesse gelten: Prozesse vererben ihre Umgebung prinzipiell an ihre Unterprozesse. Wenn du also den Pfad fuer latex korrekt setzt, dann werden auch dvips und Co auffindbar sein. Du kannst das entweder machen wie ich das gezeigt habe, also den eigenen Python-Prozess bzw. dessen Umgebung manipulieren. Oder wie lunar erwaehnte, ein env-dictionary an subprocess uebergeben. Das ist an sich sauberer, weil dadurch Seiteneffekte in deinem Prozess verhindert werden.
deets

Code: Alles auswählen

env = dict(os.environ)
env['PATH'] = '/opt/local/bin/{sep}{path}'.format(sep=os.pathsep, path=env['PATH'])
subprocess.call([...], env=env)
Was ist denn danach in env["PATH"] drin? An sich sollte das /opt/local/bin als ersten Eintrag hinzufuegen. Und fuer mich zB funktioniert das hier (achtung, das entfernt natuerlich Dinge wie /usr/bin und so aus dem Pfad, besser ist der obige Weg - aber um zu beweisen, dass es geht:

Code: Alles auswählen


import os
import tempfile
import subprocess

path = os.environ["PATH"]

env = dict(os.environ)
env['PATH'] = '/opt/local/bin'

subprocess.call(["latex", "--help"], env=env)
lunar

@deets: Der OP hat den Pfad korrekt gesetzt. Nur eben in der Konfiguration der Shell...

@woody55: ... und genau das ist hier das Problem. Im Allgemeinen gelten Umgebungsvariablen, die Du in der Konfiguration der Shell (beispielsweise "~/.bashrc") setzt, nur für die Shell, die diese Konfiguration liest. Insbesondere gelten diese Werte nicht für die graphische Oberfläche von OS X, da OS X beim Start der graphischen Oberfläche keine Shell ausführt, die diese Umgebungsvariablen dann vererben könnte, und auch selbstverständlich nicht auf irgendeine magische Art und Weise die Konfiguration der Shell ausliest [1]. Startest Du Eclipse also über Spotlight oder aus dem Dock, also allgemein irgendwie über die graphische Oberfläche, dann kennt Eclipse folglich nicht die Umgebungsvariablen, die in der Konfiguration der Shell gesetzt werden.

Es gibt jetzt mehrere Möglichkeiten, dieses Problem so mehr oder weniger zu lösen: Die einfachste und schnellste wäre, die Umgebungsvariablen in der Run-Konfiguration von Eclipse gesondert einzutragen. Dann kannst Du die Umgebungsvariablen aus der Konfiguration der Shell per "launchctl setenv" an die graphische Oberfläche übertragen, musst dazu aber immer nach dem Systemstart einmal ein Terminal ausführen. Oder Du konfigurierst die Shell so, dass die Umgebungsvariablen in jeder Shell und nicht nur in interaktiven Shells (also solchen im Terminal) gesetzt werden. Das funktioniert dann immer, geht allerdings nur, wenn Eclipse den Python-Interpreter über den Umweg einer Shell startet, und wenn die Shell überhaupt so konfiguriert werden kann. Ob ersteres der Fall ist, weiß ich nicht, letzteres ist bei "bash" nicht der Fall. Soweit ich weiß, kann nur "zsh" über "~/.zshenv" so konfiguriert werden.

[1] Unter Linux ist das anders: Dort wird oft, allerdings nicht immer, beim Start der graphischen Oberfläche irgendwann ein Shell-Skript ausgeführt, dass die Konfiguration der Shell einmal einliest, und darin enthaltene Umgebungsvariablen dann an den Prozess der graphischen Oberfläche vererbt, der dann seinerseits diese Umgebung an Prozesse vererbt, die in der graphischen Oberfläche gestartet werden.
Benutzeravatar
woody55
User
Beiträge: 13
Registriert: Freitag 4. Mai 2012, 22:00
Wohnort: /welt/graz/home

@lunar: Danke für die Erklärungen und die Tips!

In einem Terminal gestartet funktioniert alles einwandfrei. Die von dir vorgeschlagenen Work-arounds werde ich mir morgen vornehmen.

Letzten Endes wird das Programm ohnehin in einer Konsole gestartet werden, da ich keine GUI entwerfen werde.

Wenn alles läuft, lasse ich dir ein paar gute Börsentips zukommen :D

Schönen Abend!
Nach Diktat vereist.
Äh - nein, falsch.
Nach Diktat vergreist.
Mist!
Nach Dicktat ... es interessiert sowieso niemanden.
Antworten