Re: [Python-de] PYTHONPATH und sys.path

Stefan Behnel python-de@behnel.de writes:
Heißt das jetzt, du möchtest deinen Nutzern lieber aufbürden, irgendwelche Pfade von Hand herauszusuchen und explizit von der Kommandozeile zu übergeben, anstatt einfach in eine saubere Umgebung zu installieren?
Konkreter Fall: das Paket wird lokal von einem (non-Root) Nutzer installiert, der dann den Pfad mittels PYTHONPATH adaptiert. Das läuft gut, bis der Systemadministrator das Paket ebenfalls (site-local) installiert. Irgendwann gibt es ein Update, welches der lokale Nutzer installiert, um nach einer Weile festzustellen, dass der -- trotz PYTHONPATH -- immer noch die alte Version (nämlich die des Sysadmins) lädt. Jetzt entsteht an mich (als Paketautor) die Frage, wie er es erreichen kann, dass das user-installiertes Paket verwendet wird und nicht das ältere, vom Sysadmin installierte.
"Dann installiere Dir doch noch ein weiteres Paket, oder besser gleich zwei [virtualenv, virtualenvwrapper] und arbeite Dich in die Funktionsweise dieser Pakete ein" hilft da nicht gerade für Akzeptanz.
Irgendwie leuchtet mir nicht ein, warum Python (was ja letztlich für seinen pragmatischen Ansatz bekannt ist) an dieser Stelle die normale Logik "Userkonfiguration geht vor lokaler Konfiguration und die vor der Defaultkonfiguration" missachtet.
Da leuchtet mir der Vorteil nicht so direkt ein.
Der Grund ist, dass ich gar nicht selbst die Installation durchführe, sondern Nutzer darin unterstützen möchte, das Paket möglichst problemlos einzusetzen. Ein "wenn Du es selbst installierst, musst Du den PYTHONPATH entsprechend setzen" ist da ziemlich trivial; es entspricht einem "Wenn Du Deine eigenen Programme starten willst, setze PATH. Wenn Du eigene shared libs verwenden willst, setze LD_LIBRARY_PATH. Wenn Du eigene Java-Klassen verwenden willst, setze CLASSPATH". In keinem der Fälle würde man erwarten, dass sich die lokale (site-) Konfiguration vordrängelt. Nur Py-"behave as expected"-thon weicht da auf subtile Weise ab.
Viele Grüße
Ole

Am 03.02.2012 09:29, schrieb Olе Streicher:
Konkreter Fall: das Paket wird lokal von einem (non-Root) Nutzer installiert, der dann den Pfad mittels PYTHONPATH adaptiert. Das läuft gut, bis der Systemadministrator das Paket ebenfalls (site-local) installiert. Irgendwann gibt es ein Update, welches der lokale Nutzer installiert, um nach einer Weile festzustellen, dass der -- trotz PYTHONPATH -- immer noch die alte Version (nämlich die des Sysadmins) lädt. Jetzt entsteht an mich (als Paketautor) die Frage, wie er es erreichen kann, dass das user-installiertes Paket verwendet wird und nicht das ältere, vom Sysadmin installierte.
*Genau* für diese Fall habe ich den PEP #370 entwickelt und implementiert.
python setup.py install --user
Christian

Christian Heimes schrieb:
Am 03.02.2012 09:29, schrieb Olе Streicher:
Konkreter Fall: das Paket wird lokal von einem (non-Root) Nutzer installiert, der dann den Pfad mittels PYTHONPATH adaptiert. Das läuft gut, bis der Systemadministrator das Paket ebenfalls (site-local) installiert. Irgendwann gibt es ein Update, welches der lokale Nutzer installiert, um nach einer Weile festzustellen, dass der -- trotz PYTHONPATH -- immer noch die alte Version (nämlich die des Sysadmins) lädt. Jetzt entsteht an mich (als Paketautor) die Frage, wie er es erreichen kann, dass das user-installiertes Paket verwendet wird und nicht das ältere, vom Sysadmin installierte.
*Genau* für diese Fall habe ich den PEP #370 entwickelt und implementiert.
python setup.py install --user
Richtig. Oder "pip install --user mypackage". Das wurde hier vor kurzem schon diskutiert.
Viele Grüße
Markus

On 2012-02-03, Olе Streicher wrote:
Stefan Behnel python-de@behnel.de writes:
Heißt das jetzt, du möchtest deinen Nutzern lieber aufbürden, irgendwelche Pfade von Hand herauszusuchen und explizit von der Kommandozeile zu übergeben, anstatt einfach in eine saubere Umgebung zu installieren?
Konkreter Fall: das Paket wird lokal von einem (non-Root) Nutzer installiert, der dann den Pfad mittels PYTHONPATH adaptiert. Das läuft gut, bis der Systemadministrator das Paket ebenfalls (site-local) installiert. Irgendwann gibt es ein Update, welches der lokale Nutzer installiert, um nach einer Weile festzustellen, dass der -- trotz PYTHONPATH -- immer noch die alte Version (nämlich die des Sysadmins) lädt.
Das sollte meinem Verständnis nach nicht passieren. PYTHONPATH sollte an sich so verwendet werden können, wie Du es erwartest: die Verzeichnisse in PYTHONPATH werden vor denen default Verzeichnissen (Standardbibliothek, site-packages, etc.) durchsucht.
In sys.path sollte sich maximal ein weiteres Verzeichnis vor denen aus PYTHONPATH befinden, typischerweise das Verzeichnis, indem sich das Script befindet, das Python ausführen soll, bzw. das aktuelle Arbeitsverzeichnis bei interaktivem Gebrauch oder verwenden der -c Option.
Siehe http://docs.python.org/using/cmdline.html#envvar-PYTHONPATH
Meiner Erfahrung nach funktioniert PYTHONPATH auch genauso und man kann sehr gut ohne virtualenv auskommen.
Es wäre eventuell interessant herauszufinden, warum PYTHONPATH bei Dir nicht so funktioniert wie erwartet. Gibt es ein sitecustomize Modul? Falls ja, könnte das dafür verantwortlich sein. Gibt es irgendwelche *.pth Dateien, die Zeilen enthalten, die mit "import" anfangen. Solche Zeilen werden vom Interpreter ausgeführt und können in sys.path auch am Anfang Einträge einfügen, vor den Verzeichnissen aus PYTHONPATH.
Bernhard

Am 03.02.2012 12:35, schrieb Bernhard Herzog:
Das sollte meinem Verständnis nach nicht passieren. PYTHONPATH sollte an sich so verwendet werden können, wie Du es erwartest: die Verzeichnisse in PYTHONPATH werden vor denen default Verzeichnissen (Standardbibliothek, site-packages, etc.) durchsucht.
Deine Erfahrung widerspricht aber der Implementation, Designidee und auch der Realtität. Sorry, aber du liegst falsch. ;)
Wenn du die Implementation in Py_InitializeEx() verfolgst, wirst du feststellen, dass in Py_GetPath() die Umgebungsvariable PYTHONPATH vor initsite() ausgewertet wird. Das wurde gemacht, damit man eine eigene site.py bzw. sitecustomize.py über PYTHONPATH einfügen kann. Der Code in site.py parst alle *.pth Dateien in den site-packages und fügt diese *vorne* in sys.path ein.
Probier es mal selbst aus:
$ PYTHONPATH=/tmp python2.7 -c "import sys; print sys.path" ['', '/usr/local/lib/python2.7/dist-packages/selenium-2.0rc2-py2.7.egg', '/usr/local/lib/python2.7/dist-packages/rdflib-3.1.0-py2.7.egg', '/usr/local/lib/python2.7/dist-packages/psutil-0.3.0-py2.7-linux-x86_64.egg', '/tmp', '/usr/lib/python2.7', '/usr/lib/python2.7/plat-linux2', '/usr/lib/python2.7/lib-tk', '/usr/lib/python2.7/lib-old', '/usr/lib/python2.7/lib-dynload', '/home/heimes/.local/lib/python2.7/site-packages', '/usr/local/lib/python2.7/dist-packages', '/usr/lib/python2.7/dist-packages', '/usr/lib/python2.7/dist-packages/PIL', '/usr/lib/pymodules/python2.7/gtk-2.0', '/usr/lib/python2.7/dist-packages/gtk-2.0', '/usr/lib/pymodules/python2.7']
Christian

On 2012-02-03, Christian Heimes wrote:
Am 03.02.2012 12:35, schrieb Bernhard Herzog:
Das sollte meinem Verständnis nach nicht passieren. PYTHONPATH sollte an sich so verwendet werden können, wie Du es erwartest: die Verzeichnisse in PYTHONPATH werden vor denen default Verzeichnissen (Standardbibliothek, site-packages, etc.) durchsucht.
Deine Erfahrung widerspricht aber der Implementation, Designidee und auch der Realtität. Sorry, aber du liegst falsch. ;)
Bei mir verhält sich python so wie beschrieben.
Wenn du die Implementation in Py_InitializeEx() verfolgst, wirst du feststellen, dass in Py_GetPath() die Umgebungsvariable PYTHONPATH vor initsite() ausgewertet wird. Das wurde gemacht, damit man eine eigene site.py bzw. sitecustomize.py über PYTHONPATH einfügen kann.
Soweit sollte das kein Problem sein. Das erfordet ja nicht, dass die mit *.pth hinzugefügten Verzeichnisse vorne stehen müssen.
Der Code in site.py parst alle *.pth Dateien in den site-packages und fügt diese *vorne* in sys.path ein.
Das entspräche nicht dem dokumentierten Verhalten.
Wo ist das im übrigen implementiert? In site.py finde ich diverse sys.path.append Aufrufe aber keinen Code, der etwas am Anfang von sys.path einfügt. Vielleicht habe ich es übersehen. Ich habe in Python 2.6, 2.7 und 3.2 nachgeschaut.
Probier es mal selbst aus:
$ PYTHONPATH=/tmp python2.7 -c "import sys; print sys.path"
$ PYTHONPATH=/tmp python2.7 -c "import sys; print sys.path" ['', '/tmp', '$PREFIX/lib/python27.zip', '$PREFIX/lib/python2.7', '$PREFIX/lib/python2.7/plat-linux2', '$PREFIX/lib/python2.7/lib-tk', '$PREFIX/lib/python2.7/lib-old', '$PREFIX/lib/python2.7/lib-dynload', '$PREFIX/lib/python2.7/site-packages', '$PREFIX/lib/python2.7/site-packages/PIL', '$PREFIX/lib/python2.7/site-packages/setuptools-0.6c11-py2.7.egg-info']
Um es übersichtlicher zu machen habe ich das etwas längliche Installationsverzeichnis durch $PREFIX ersetzt. Die mit *.pth-Dateien hinzugefügten Verzeichnisse sind am Ende.
['', '/usr/local/lib/python2.7/dist-packages/selenium-2.0rc2-py2.7.egg', '/usr/local/lib/python2.7/dist-packages/rdflib-3.1.0-py2.7.egg', '/usr/local/lib/python2.7/dist-packages/psutil-0.3.0-py2.7-linux-x86_64.egg ', '/tmp', '/usr/lib/python2.7', '/usr/lib/python2.7/plat-linux2', '/usr/lib/python2.7/lib-tk', '/usr/lib/python2.7/lib-old', '/usr/lib/python2.7/lib-dynload', '/home/heimes/.local/lib/python2.7/site-packages', '/usr/local/lib/python2.7/dist-packages', '/usr/lib/python2.7/dist-packages', '/usr/lib/python2.7/dist-packages/PIL', '/usr/lib/pymodules/python2.7/gtk-2.0', '/usr/lib/python2.7/dist-packages/gtk-2.0', '/usr/lib/pymodules/python2.7']
Ist das das Python von Debian? Macht Debian da irgendwas anders als ein vanilla Python?
Bernhard

Am 03.02.2012 13:57, schrieb Bernhard Herzog:
Wo ist das im übrigen implementiert? In site.py finde ich diverse sys.path.append Aufrufe aber keinen Code, der etwas am Anfang von sys.path einfügt. Vielleicht habe ich es übersehen. Ich habe in Python 2.6, 2.7 und 3.2 nachgeschaut.
Ich hätte vielleicht der Vollständigkeit erwähnen sollen, dass das Verhalten eine Spezialität von easy-install.pth und setuptools ist. Sorry für die Verwirrung! Normale .pth Dateien verhalten sich anders.
easy-install.pth fügt die Eggs und weiteren Pfade immer vor den übrigen Pfaden ein, dmait eine virtuelle Umgebung global installierte Pythonpakete überschreiben kann. Erweiterungen wie virtualenv und pip machen es übrigens genau so. Der Einfachheit halber wird dabei PYTHONPATH nicht berücksichtigt.
.pth haben einen Sonderfall. Wenn eine Zeile mit dem String "import" gefolgt von einem Whitespace oder Tab startet, dann wird diese Zeile über exec() ausgeführt.
Eine easy-install.pth sieht wie folgt aus:
import sys; sys.__plen = len(sys.path) ./some.egg ./other.egg import sys; new=sys.path[sys.__plen:]; del sys.path[sys.__plen:]; p=getattr(sys,'__egginsert',0); sys.path[p:p]=new; sys.__egginsert = p+len(new)
Christian

On 2012-02-03, Christian Heimes wrote:
Am 03.02.2012 13:57, schrieb Bernhard Herzog:
Wo ist das im übrigen implementiert? In site.py finde ich diverse sys.path.append Aufrufe aber keinen Code, der etwas am Anfang von sys.path einfügt. Vielleicht habe ich es übersehen. Ich habe in Python 2.6, 2.7 und 3.2 nachgeschaut.
Ich hätte vielleicht der Vollständigkeit erwähnen sollen, dass das Verhalten eine Spezialität von easy-install.pth und setuptools ist. Sorry für die Verwirrung! Normale .pth Dateien verhalten sich anders.
Daran liegts also.
easy-install.pth fügt die Eggs und weiteren Pfade immer vor den übrigen Pfaden ein, dmait eine virtuelle Umgebung global installierte Pythonpakete überschreiben kann. Erweiterungen wie virtualenv und pip machen es übrigens genau so.
Soweit scheint mir das sinnvoll.
Der Einfachheit halber wird dabei PYTHONPATH nicht berücksichtigt.
Das allerdings nicht wirklich. PYTHONPATH sollte immer vor den per default durchsuchten Verzeichnissen durchsucht werden. Es kann natürlich sein, dass es nicht einfach wäre, das in Tools wie virtualenv zu erreichen.
Na ja. Ein weiterer Grund, easy_install, virtualenv und Konsorten möglichst nicht zu verwenden.
Eine easy-install.pth sieht wie folgt aus:
import sys; sys.__plen = len(sys.path) ./some.egg ./other.egg import sys; new=sys.path[sys.__plen:]; del sys.path[sys.__plen:]; p=getattr(sys,'__egginsert',0); sys.path[p:p]=new; sys.__egginsert = p+len(new)
Au weia. Allerdings, wenn man sys.__egginsert passend vorbelegen könnte, wären die hinzugefügten Verzeichnisse nicht am Anfang.
Bernhard
participants (4)
-
Bernhard Herzog
-
Christian Heimes
-
Markus Zapke-Gründemann
-
ole-usenet-spam@gmx.net