Hallo,
ich will für Python(2.x und 3) ein Interface für Prolog erstellen. Dies lässt sich am schönsten realisieren, in dem ich einfach alle Methoden aus den C-Dateien von einer Prolog Implementierung, in einem Python Paket anbinde. Dies ließe sich ja theoretisch über ctypes oder Cython lösen, doch was ist jetzt die bessere Lösung?
Ich hab bereits auf #python gefragt, doch dort hieß es immer einfach nur "benutz Cython", aber ohne wirklich zu begründen.
Was wichtig für mich ist:
- Einfaches setup des interfaces
- Platformunabhängigkeit(sowohl entwickeln als auch benutzen)
- Einfachheit des Codes(der ctypes Wrapper Code scheint mir meist sehr häßlich; aber cython ist nochmal eine extra Sprache)
Ich hab speziell die Platformunabhängigkeite erwähnt, weil ich gehört habe, es soll nicht so einfach sein unter Windows mit Cython eine C-Erweiterung für Python zu kompilieren. Auch wenn man MinGW installiert, denn anscheinend müsste man dann python etc auch mit gcc kompilieren. Ich selbst weiß nicht ob das stimmt, denn ich benutz kein Windows. Wäre nett wenn mir da jemand Auskunft geben könnte.
Danke im voraus
Interface für andere Sprache: Cython vs. ctypes
@naeg: Sofern die anzubindende Bibliothek eine reine C-Bibliothek ist, halte ich ctypes für die bessere Wahl. Ich sehe den Sinn von cython auch nicht so sehr darin, Anbindungen zu schreiben, sondern eher darin, Python-Quelltext durch Übersetzung in C zu beschleunigen.
Eine Anbindung auf Basis von ctypes kommt mit reinem Python und den Mitteln der Standardbibliothek aus, cython dagegen braucht immer einen Compiler. Das kann selbst unter Linux, wo Compilieren nun nichts ungewöhnliches ist, problematisch sein, beispielsweise auf einen angemieteten Webserver, wo die Administratorrechte zur Nachinstallation des Compilers und der Header-Dateien von CPython fehlen. Zudem gibt es ja dank komfortabler Distributionen wie Ubuntu auch viele Linux-Nutzer, die gar nicht mehr so genau wissen, was kompilieren ist, sich aber dennoch an Python versuchen
Wiewohl sowohl ctypes als auch cython theoretisch plattformunabhängig sind, ist der Einsatz von ctypes auf vielen Plattformen aus genanntem Grund wesentlich einfacher, sowohl für Python-Entwickler als auch für spätere Nutzer eine mithilfe Deiner Anbindung geschriebenen Anwendung.
Ich persönlich kann Deine Auffassung, ctypes-Quelltext wäre „hässlich“ nicht nachvollziehen. Im Rahmen der Grenzen, welche die Ausdrucksschwäche von C setzt, ist ctypes recht schön, und vor allem verhältnismäßig einfach zu benutzen. C ist nun mal eine ziemlich schwache Sprache, cython macht sie auch nicht schöner.
Eine Anbindung auf Basis von ctypes kommt mit reinem Python und den Mitteln der Standardbibliothek aus, cython dagegen braucht immer einen Compiler. Das kann selbst unter Linux, wo Compilieren nun nichts ungewöhnliches ist, problematisch sein, beispielsweise auf einen angemieteten Webserver, wo die Administratorrechte zur Nachinstallation des Compilers und der Header-Dateien von CPython fehlen. Zudem gibt es ja dank komfortabler Distributionen wie Ubuntu auch viele Linux-Nutzer, die gar nicht mehr so genau wissen, was kompilieren ist, sich aber dennoch an Python versuchen
Wiewohl sowohl ctypes als auch cython theoretisch plattformunabhängig sind, ist der Einsatz von ctypes auf vielen Plattformen aus genanntem Grund wesentlich einfacher, sowohl für Python-Entwickler als auch für spätere Nutzer eine mithilfe Deiner Anbindung geschriebenen Anwendung.
Ich persönlich kann Deine Auffassung, ctypes-Quelltext wäre „hässlich“ nicht nachvollziehen. Im Rahmen der Grenzen, welche die Ausdrucksschwäche von C setzt, ist ctypes recht schön, und vor allem verhältnismäßig einfach zu benutzen. C ist nun mal eine ziemlich schwache Sprache, cython macht sie auch nicht schöner.
@naeg: Also ich würde ja `ctypes` den Vorzug geben -- eben weil man da nichts kompilieren muss. Bei Cython müsste man ja entweder für alle Python-Versionen, die Endbenutzer verwenden möchten, ein Binärpaket für verschiedene Plattformen bereits stellen -- also zumindest für Windows, weil da die Benutzer es in der Regel überhaupt nicht gewohnt sind zum Installieren einen C-Compiler zur Hand zu haben.
Gegen `ctypes` sprechen in der Regel zu komplexe Headerdateien, die sich nur mühsam in Python-Code umschreiben lassen, und wenn der Overhead der dynamischen Aufrufe zu gross wird.
Gegen `ctypes` sprechen in der Regel zu komplexe Headerdateien, die sich nur mühsam in Python-Code umschreiben lassen, und wenn der Overhead der dynamischen Aufrufe zu gross wird.
Derzeit tendier ich auch eher zu ctypes.
Jedoch gibts es auch noch Skripts von ctypes direkt. (http://starship.python.net/crew/theller ... degen.html)
Ich hab mir letzteres noch nicht genau angesehen, werde ich aber heute hoffentlich noch machen.
Danke schonmal für eure Meinungen, bin noch offen für weitere
Gibt es da nicht Skripte die das generieren? Ich hab zuerst ctypesgen auf Google Code gefunden, doch das produziert sehr viel Code, und nicht nur reine Funktionsanbindungen(was eig. fast alles ist was ich brauche).BlackJack hat geschrieben: Gegen `ctypes` sprechen in der Regel zu komplexe Headerdateien, die sich nur mühsam in Python-Code umschreiben lassen, ...
Jedoch gibts es auch noch Skripts von ctypes direkt. (http://starship.python.net/crew/theller ... degen.html)
Ich hab mir letzteres noch nicht genau angesehen, werde ich aber heute hoffentlich noch machen.
Danke schonmal für eure Meinungen, bin noch offen für weitere
mfg naeg
@naeg: Die Skripte von `ctypes` sind wohl schon ziemlich alt und werden nicht mehr weiter entwickelt.
Ich musste bis jetzt noch keine wirklich grosse API anbinden und mag handgeschriebenen Code eigentlich lieber als solche generierten Quelltextmonster.
Ausserdem mache ich da in der Regel auch Sachen, die ein automatisches Werkzeug wohl nicht alle leisten kann.
Wenn die C-Funktionen und Konstanten zum Beispiel einen Prefix haben, weil C keine Namensräume kennt, dann entferne ich den in der Regel.
Ich weise bei Funktionen die Fehlerwerte zurück geben auch gerne das `errcheck`-Attribut zu, um Fehlerwerte in Ausnahmen umzuwandeln.
Ich baue oft eine "pythonische" API darauf auf, und einige Funktionen kann man direkt anbieten und andere sind Bausteine für weitere Funktionen oder Methoden. Da muss man also entscheiden ob ein `_` vor den Namen kommt, oder nicht.
Ich musste bis jetzt noch keine wirklich grosse API anbinden und mag handgeschriebenen Code eigentlich lieber als solche generierten Quelltextmonster.
Ausserdem mache ich da in der Regel auch Sachen, die ein automatisches Werkzeug wohl nicht alle leisten kann.
Wenn die C-Funktionen und Konstanten zum Beispiel einen Prefix haben, weil C keine Namensräume kennt, dann entferne ich den in der Regel.
Ich weise bei Funktionen die Fehlerwerte zurück geben auch gerne das `errcheck`-Attribut zu, um Fehlerwerte in Ausnahmen umzuwandeln.
Ich baue oft eine "pythonische" API darauf auf, und einige Funktionen kann man direkt anbieten und andere sind Bausteine für weitere Funktionen oder Methoden. Da muss man also entscheiden ob ein `_` vor den Namen kommt, oder nicht.
Das ist mir auch gerade aufgefallen.
Ich glaube ich werde es dann halt von Hand machen. Aber eine Frage habe ich noch:
Wenn ich eine lib mit CDLL() laden will, dann wird die lib scheinbar nur im LD_LIBRARY_PATH gesucht, und somit wird meine lib im Ordner /usr/lib/swipl-VERSION/lib/ARCH/ nicht gefunden. Verlangen einen Symlink zu erstellen scheint mir nicht so gut, aber pkg-config sollte die lib ja finden(tut es bei mir). Soll ich nun einfach mit subprocess pkg-config aufrufen und so den Pfad rausfinden?
Hab dazu auch das hier gefunden: http://www.python-forum.de/viewtopic.php?f=11&t=19167
Ich glaube ich werde es dann halt von Hand machen. Aber eine Frage habe ich noch:
Wenn ich eine lib mit CDLL() laden will, dann wird die lib scheinbar nur im LD_LIBRARY_PATH gesucht, und somit wird meine lib im Ordner /usr/lib/swipl-VERSION/lib/ARCH/ nicht gefunden. Verlangen einen Symlink zu erstellen scheint mir nicht so gut, aber pkg-config sollte die lib ja finden(tut es bei mir). Soll ich nun einfach mit subprocess pkg-config aufrufen und so den Pfad rausfinden?
Hab dazu auch das hier gefunden: http://www.python-forum.de/viewtopic.php?f=11&t=19167
mfg naeg
pkg-config ist eigentlich nicht zuständig für das Auffinden von Bibliotheken zur Laufzeit eines Programms. Es gibt lediglich die für den Compiler und den Linker benötigten Flags aus, ist also eher für die Übersetzung eines Programms gedacht. Man kann pkg-config zwar für diesen Zweck (miss-)brauchen, aber das muss nicht funktionieren. pkg-config ist beileibe nicht immer installiert, vor allem nicht unter Windows.
Bibliotheken, die auch für Drittanwendungen gedacht sind und nicht lediglich internen Zwecken dienen, sollten eigentlich im Pfad des Linkers liegen.
Bibliotheken, die auch für Drittanwendungen gedacht sind und nicht lediglich internen Zwecken dienen, sollten eigentlich im Pfad des Linkers liegen.
@naeg: Findet `ctypes.util.find_library()` die Bibliothek denn? Und bis Du sicher, dass Du keine `/usr/lib/libpl.so.{version}` hast? Ich habe das jedenfalls (Ubuntu).
@BlackJack: Unter Arch installiert das Paket für SWI-Prolog keine "libpl", und "find_library()" findet "libswipl" auch nicht.
@lunar: Und wo wird da die .so installiert? Und wird der Pfad in die Binärdatei vom Prolog-Interpreter einkompiliert oder gar die Bibliothek statisch kompiliert? Im letzten Fall könnte man da von `ctypes` aus ja gar nicht heran kommen. Im Äquivalent von `/usr/lib/swipl-VERSION/lib/ARCH/` liegt bei mir auch nur eine Bibliothek zum statischen Linken (`libpl.a`).
@BlackJack: swipl ist in der Tat nur eine statische Bibliothek, darauf hatte ich gar nicht geachtet. Eine dynamische Bibliothek namens libpl gibt es dagegen gar nicht. Offenbar ist das Paket unter Arch nicht so kompiliert worden, dass es eine dynamische Bibliothek installiert. Ich schätze mal, dass der Verwalter dieses Pakets schlicht nur vergessen hat, diese Option bei der Übersetzung zu aktivieren.
Ach ja, es gibt mit pyswip bereits eine Anbindung an SWI Prolog über ctypes. Die wird zwar nicht mehr gewartet, aber man könnte ja darauf aufbauen (sofern die Lizenz kein Problem darstellt).
Ach ja, es gibt mit pyswip bereits eine Anbindung an SWI Prolog über ctypes. Die wird zwar nicht mehr gewartet, aber man könnte ja darauf aufbauen (sofern die Lizenz kein Problem darstellt).
Da ich ebenfalls ein Archer bin, kann ich dir sagen, dass du da nicht ganz richtig liegstlunar hat geschrieben:@BlackJack: swipl ist in der Tat nur eine statische Bibliothek, darauf hatte ich gar nicht geachtet. Eine dynamische Bibliothek namens libpl gibt es dagegen gar nicht. Offenbar ist das Paket unter Arch nicht so kompiliert worden, dass es eine dynamische Bibliothek installiert. Ich schätze mal, dass der Verwalter dieses Pakets schlicht nur vergessen hat, diese Option bei der Übersetzung zu aktivieren.
Die shared library befindet sich, bei mir, in /usr/lib/swipl-5.10.2/lib/x86_64-linux/libswipl.so. Jedoch findet CDLL hier nicht, pkg-config schon.
Bei Arch heißt die lib eben libswipl, nicht libpl. Aber das Paket wird ganz normal mit ./configure, make und make install installiert, daher vermute ich, dass swi-prolog die lib umbenannt hat.(Hier läuft Version 5.10.2)
Genau an diesem Projekt hab ich begonnen zu arbeitenlunar hat geschrieben:Ach ja, es gibt mit pyswip bereits eine Anbindung an SWI Prolog über ctypes. Die wird zwar nicht mehr gewartet, aber man könnte ja darauf aufbauen (sofern die Lizenz kein Problem darstellt).
Übrigens, find_library() ist was ich brauche und funktioniert:
Code: Alles auswählen
>>> from ctypes.util import find_library
>>> find_library("swipl")
'libswipl.so.5.10.2
mfg naeg
@naeg: Du kannst ruhig davon ausgehen, dass ich mir den Inhalt des Bibliotheksverzeichnisses der Prolog-Installation angeschaut habe, bevor ich meinen Beitrag verfasst habe. Auf meinem System existiert keine dynamische Bibliothek namens "swipl":
Offenbar gibt es einen Unterschied zwischen den Paketen für 32- und 64-Bit Architekturen.
Was die Rückgabe von "find_library()" angeht, so kannst Du diese einfach an "CDLL" übergeben, unabhängig davon, ob der Pfad absolut ist oder nicht. Relative Pfade sind relativ zu den Suchverzeichnissen des Laufzeitbinders, und werden daher problemlos auf die entsprechenden Bibliotheksdateien abgebildet. Achte allerdings darauf, die Rückgabe von "find_library()" gegen "None" zu prüfen. "find_library()" gibt diesen Wert zurück, falls die Bibliothek nicht gefunden wurde, allerdings kommt "CDLL" damit nicht zurecht. Somit könnte eine Funktion zum Laden der Bibliothek etwa so aussehen:
In pyswip könnte man übrigens erst einmal aufräumen. Die Namenskonvention der Schnittstelle entspricht nicht PEP 8, es fehlen Docstrings und Unittests, "easy.py" an manchen Stellen umständlich, "core.py" ist ein einziges Durcheinander ... dafür, dass der Autor so viel Aufhebens um den richtigen Maintainer macht, ist der Quelltext ziemlich unschön.
Code: Alles auswählen
$ find /usr/lib/swipl-5.10.2/lib/i686-linux/ -name '*swipl*'
/usr/lib/swipl-5.10.2/lib/i686-linux/libswipl.a
Was die Rückgabe von "find_library()" angeht, so kannst Du diese einfach an "CDLL" übergeben, unabhängig davon, ob der Pfad absolut ist oder nicht. Relative Pfade sind relativ zu den Suchverzeichnissen des Laufzeitbinders, und werden daher problemlos auf die entsprechenden Bibliotheksdateien abgebildet. Achte allerdings darauf, die Rückgabe von "find_library()" gegen "None" zu prüfen. "find_library()" gibt diesen Wert zurück, falls die Bibliothek nicht gefunden wurde, allerdings kommt "CDLL" damit nicht zurecht. Somit könnte eine Funktion zum Laden der Bibliothek etwa so aussehen:
Code: Alles auswählen
def load_prolog_library():
for candidate in ('swipl', 'pl'):
full_name = find_library(candidate)
if full_name:
break
else:
raise ImportError('No library named pl or swipl')
return CDLL(full_name)
Hab extra 32 und 64 bit Paket runtergeladen und entpackt - tatsächlich gibts die lib nur in dem x86_64 Paket. Eben mal bei SWI-Prolog nachgefragt.lunar hat geschrieben:@naeg: Du kannst ruhig davon ausgehen, dass ich mir den Inhalt des Bibliotheksverzeichnisses der Prolog-Installation angeschaut habe, bevor ich meinen Beitrag verfasst habe. Auf meinem System existiert keine dynamische Bibliothek namens "swipl":Offenbar gibt es einen Unterschied zwischen den Paketen für 32- und 64-Bit Architekturen.Code: Alles auswählen
$ find /usr/lib/swipl-5.10.2/lib/i686-linux/ -name '*swipl*' /usr/lib/swipl-5.10.2/lib/i686-linux/libswipl.a
Also ist es nicht anders möglich, als einen Symlink in /usr/lib zu erstellen?lunar hat geschrieben: Was die Rückgabe von "find_library()" angeht, so kannst Du diese einfach an "CDLL" übergeben, unabhängig davon, ob der Pfad absolut ist oder nicht. Relative Pfade sind relativ zu den Suchverzeichnissen des Laufzeitbinders, und werden daher problemlos auf die entsprechenden Bibliotheksdateien abgebildet. Achte allerdings darauf, die Rückgabe von "find_library()" gegen "None" zu prüfen. "find_library()" gibt diesen Wert zurück, falls die Bibliothek nicht gefunden wurde, allerdings kommt "CDLL" damit nicht zurecht.
Ich glaube ich werde es einfach mit find_library() probieren(mit "pl" und "swipl") und wenn das nicht funktioniert, missbrauche ich zur Not pkg-config, sollte das auch nicht funktionieren, gibt es eine Meldung die auffordert einen Symlink in /usr/lib zu erstellen.
Danke für den Hinweis. Werde mich darum noch kümmern, der ganze Teil mit den ctypes muss sowieso noch überarbeitet werden, denn unter x86_64 gibts segfaults beim assertz().lunar hat geschrieben: In pyswip könnte man übrigens erst einmal aufräumen. Die Namenskonvention der Schnittstelle entspricht nicht PEP 8, es fehlen Docstrings und Unittests, "easy.py" an manchen Stellen umständlich, "core.py" ist ein einziges Durcheinander ... dafür, dass der Autor so viel Aufhebens um den richtigen Maintainer macht, ist der Quelltext ziemlich unschön.
mfg naeg
@raed: Ich habe nichts gesagt von einem Symlink nach "/usr/lib/". "/usr/lib/" ist nicht der einzige Pfad, in dem der Laufzeitbinder nach Bibliotheken sucht. Hast Du denn überhaupt probiert, die Rückgabe von "find_library()" einfach an "CDLL()" zu übergeben, um die Bibliothek zu laden, so wie in meinem Beispiel gezeigt?
Die Entwickler von SWI-Prolog selbst sind im Übrigen die falsche Anlaufstelle für dieses Problem. Das ist erst einmal Sache des Maintainers des entsprechenden Pakets in Arch Linux.
Die Entwickler von SWI-Prolog selbst sind im Übrigen die falsche Anlaufstelle für dieses Problem. Das ist erst einmal Sache des Maintainers des entsprechenden Pakets in Arch Linux.
Nein, das funktioniert nicht.lunar hat geschrieben:@naeg: Ich habe nichts gesagt von einem Symlink nach "/usr/lib/". "/usr/lib/" ist nicht der einzige Pfad, in dem der Laufzeitbinder nach Bibliotheken sucht. Hast Du denn überhaupt probiert, die Rückgabe von "find_library()" einfach an "CDLL()" zu übergeben, um die Bibliothek zu laden, so wie in meinem Beispiel gezeigt?
Ich hab selbst einen Symlink in /usr/lib erstellt, dann sieht es wie folgt aus:
Code: Alles auswählen
>>> find_library("swipl")
'libswipl.so.5.10.2'
>>> CDLL(find_library("swipl"))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python3.2/ctypes/__init__.py", line 340, in __init__
self._handle = _dlopen(self._name, mode)
OSError: libswipl.so.5.10.2: cannot open shared object file: No such file or directory
>>> CDLL("libswipl.so")
<CDLL 'libswipl.so', handle ... at ...>
Der Symlink:
Code: Alles auswählen
lrwxrwxrwx 1 root root 50 8. Mär 17:18 /usr/lib/libswipl.so -> /usr/lib/swipl-5.10.2/lib/x86_64-linux/libswipl.so
Ich hab in #archlinux.de nachgefragt, weil ich mir nicht sicher war und da hat man mir gesagt, dass die symlinks in /usr/lib Aufgabe des Projekts sind, nicht der Distribution. Jedoch habe ich mit Jan von SWI-Prolog zuvor schon wegen PySWIP geschrieben, und er hat gesagt er wolle mir helfen wo er kann.lunar hat geschrieben: Die Entwickler von SWI-Prolog selbst sind im Übrigen die falsche Anlaufstelle für dieses Problem. Das ist erst einmal Sache des Maintainers des entsprechenden Pakets in Arch Linux.
mfg naeg
Ich nehme mal an, dass Du eine symbolische Verknüpfung auf eine weitere symbolische Verknüpfung erstellt hast, und "find_library()" „zu weit“ auflöst. Versuche mal, die Verknüpfung direkt auf die Bibliothek mit dem vollständigen Namen der Bibliothek anzulegen, also:
Und was das Paket angeht, wieso fragst Du überall, nur nicht den, der sich mit dem Paket selbst auskennt, nämlich den Maintainer? Dessen Mailadresse steht ja nicht aus Spaß an der Freude im PKGBUILD und in der Paketdatenbank
Code: Alles auswählen
/usr/lib/libswipl.so.5.10.2 -> /usr/lib/swipl-5.10.2/lib/x86_64-linux/libswipl.so.5.10.2
Wenn ich den symlink so erstelle:lunar hat geschrieben:Ich nehme mal an, dass Du eine symbolische Verknüpfung auf eine weitere symbolische Verknüpfung erstellt hast, und "find_library()" „zu weit“ auflöst. Versuche mal, die Verknüpfung direkt auf die Bibliothek mit dem vollständigen Namen der Bibliothek anzulegen, also:Code: Alles auswählen
/usr/lib/libswipl.so.5.10.2 -> /usr/lib/swipl-5.10.2/lib/x86_64-linux/libswipl.so.5.10.2
Code: Alles auswählen
>>> find_library("swipl")
>>> find_library("swipl.5.10.2")
>>> find_library("swipl.so.5.10.2")
>>> find_library("libswipl.so.5.10.2")
>>> find_library("swipl.so")
Dem hab ich auch bereits eine Email geschrieben, er hat mir nur gesagt, dass die shared libs installiert sind und gab mir den Output von pkg-config mit(dadurch bin ich eben auf die Idee gekommen pkg-config zu verwenden).lunar hat geschrieben:Und was das Paket angeht, wieso fragst Du überall, nur nicht den, der sich mit dem Paket selbst auskennt, nämlich den Maintainer? Dessen Mailadresse steht ja nicht aus Spaß an der Freude im PKGBUILD und in der Paketdatenbank
mfg naeg
-
- Python-Forum Veteran
- Beiträge: 16025
- Registriert: Freitag 20. Juni 2003, 16:30
- Kontaktdaten:
Da ist sie wieder, die großartige Qualität von Arch-Paketen Ich würde, um ehrlich zu sein, einfach einen Bugreport an Arch schreiben und sie sonst ignorieren. Sie können sich ja wieder melden wenn sie ihren Murks ausgebessert haben.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Was hat das mit der Qualität von Arch Linux Paketen zu tun?Leonidas hat geschrieben:Da ist sie wieder, die großartige Qualität von Arch-Paketen Ich würde, um ehrlich zu sein, einfach einen Bugreport an Arch schreiben und sie sonst ignorieren. Sie können sich ja wieder melden wenn sie ihren Murks ausgebessert haben.
Arch Linux will alle Pakete möglichst unmodifiziert an Upstream halten. Da SWI-Prolog keinen Symlink in /usr/lib (o.ä.) erstellt, gibts diesen symlink eben nicht. Wenn man mal kurz nachdenkt ist die Idee mit an Upstream halten nicht blöd
Und das mit den shared libs: Es ist tatsächlich so, dass die x86 Version die shared lib nicht standardmäßig dabei hat, aber die x86_64 schon. Der Grund dafür ist, dass die x86 mit shared libs laut Entwickler ca. 10% langsamer ist.
Jan hat mir auch noch einen Tipp gegeben. Mit "swipl --dump-runtime-variables" kann ich mir den Pfad zur lib zusammenbasteln. Dies funktioniert aber wiederum nur, wenn swipl in PATH ist, was ich bei Windows ja nicht voraussetzen kann.
mfg naeg