ctypes: Fremden Typ als Argument nutzen

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.
Benutzeravatar
snafu
User
Beiträge: 5836
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Montag 1. Juni 2009, 15:28

Hi,

bei meinen Versuchen, die WebKit-API mit möglichst wenigen Abhängigkeiten anzusrpechen, probiere ich mich derzeit an einem Weg per [mod]ctypes[/mod]:

Code: Alles auswählen

In [1]: import ctypes

In [2]: from ctypes.util import find_library

In [3]: webkit = ctypes.CDLL(find_library('webkit-1.0'))

In [4]: webkit.webkit_network_request_new
Out[4]: <_FuncPtr object at 0x8bf9ecc>
Die Funktion kann also grundsätzlich angesprochen werden (da ich keine Dokumentation fand, habe ich in den Headerdateien gestöbert) und ist so definiert:

Code: Alles auswählen

WEBKIT_API WebKitNetworkRequest *
webkit_network_request_new      (const gchar          *uri);
So wie ich das sehe, kann hier eine beliebige URL mitgegeben werden, um so ein Request-Objekt zu erhalten (oder wie auch immer man das in C nennen würde).

Die Frage ist jetzt nur: Wie sage ich `ctypes`, dass es meinen String als `gchar` übergeben soll? In der Moduldokumentation wird zwar erklärt, wie man "normale" C-Typen benutzt, die ja über die Attribute von `ctypes` genutzt werden können, jedoch finde ich dort nichts über fremde Typen. :(

EDIT: Achso, als Abhängigkeit habe ich das Debianpaket `libwebkit-1.0-1` installiert.
BlackJack

Montag 1. Juni 2009, 15:55

Du musst halt herausfinden wie der Typ `gchar` definiert ist. Ich würde mal auf einen Alias für gewöhnliche `char`\s tippen!?

Was Du Da bekommst, ist übrigens in C ausgedrückt ein Zeiger auf eine `WebKitNetworkRequest`-Struktur.
Benutzeravatar
snafu
User
Beiträge: 5836
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Dienstag 2. Juni 2009, 06:20

Ja, richtig. Die `gtypes.h` definiert `gchar` so:

Code: Alles auswählen

typedef char   gchar;
Das `const gchar *uri` von oben entspricht doch dann einem `c_char_p`, oder?

Ich habe mal beides versucht:

Code: Alles auswählen

In [21]: webkit.webkit_network_request_new.argtypes = [ctypes.c_char]

In [22]: webkit.webkit_network_request_new('http://www.google.de/')
---------------------------------------------------------------------------
ArgumentError                             Traceback (most recent call last)

/home/sebastian/<ipython console> in <module>()

ArgumentError: argument 1: <type 'exceptions.TypeError'>: wrong type

In [23]: webkit.webkit_network_request_new.argtypes = [ctypes.c_char_p]

In [24]: webkit.webkit_network_request_new('http://www.google.de/')

(process:4129): GLib-GObject-CRITICAL **: /build/buildd-glib2.0_2.20.1-2-i386-hGzT8z/glib2.0-2.20.1/gobject/gtype.c:2458: 
initialization assertion failed, use IA__g_type_init() prior to this function

(process:4129): GLib-CRITICAL **: g_once_init_leave: assertion `initialization_value != 0' failed
...und danach komme ich nicht mehr in die Shell zurück. Strg+C bewirkt gar nichts und ansonsten kann ich Buchstaben eingeben, die zwar da stehen, aber nichts bewirken. Auch nicht nach <Return>.

Oder habe ich da was falsch verstanden? Okay, die Exception wird wahrscheinlich geworfen, weil er vorher sieht, dass er eben keinen einzelnen `char` bekommt. Aber unabhängig davon: Muss man vielleicht einen eigenen Typen erstellen, der als `gchar` benannt und halt vom Typ `c_char` ist? Wie würde man das machen?

EDIT: Das drückt allerdings immer noch keinen Zeiger aus. Ich bin verwirrt gerade. :(
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

Dienstag 2. Juni 2009, 07:25

Lies die Fehlermeldung, er meckert beim 2. Mal gar nicht über den Pointer.
initialization assertion failed, use IA__g_type_init() prior to this function
Laut http://www.mail-archive.com/gtk-app-dev ... 10670.html fehlt da ein gtk_init, ansonsten würde ich mich einfach mal an die API-Doku halten, demnach ist diese Funktion zumindest nicht öffentlich.
Benutzeravatar
snafu
User
Beiträge: 5836
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Dienstag 2. Juni 2009, 08:47

Klappt leider nicht:

Code: Alles auswählen

#!/usr/bin/env python

import ctypes
from ctypes.util import find_library

gtk = ctypes.CDLL(find_library('gtk-x11-2.0'))

gtk.gtk_init()

Code: Alles auswählen

~$ ./test.py
Speicherzugriffsfehler
ansonsten würde ich mich einfach mal an die API-Doku halten, demnach ist diese Funktion zumindest nicht öffentlich.
Ja, schon. Aber ich habe bis jetzt keinen anderen Weg gefunden, um einen NetworkRequest direkt initialisieren zu können. Vielleicht geht es auch ganz einfach nicht so ohne Weiteres.

EDIT: Moment, das war viel zu voreilig. Argumente wollen ja auch noch mitgegeben werden...
BlackJack

Dienstag 2. Juni 2009, 08:56

@snafu: Schau doch einfach mal in die Doku, da wirst Du sehen, dass `gtk_init()` gerne zwei Argumente hätte. Du gibtst keine an, also werden einfach zwei Werte von Stack genommen und als Zeiger auf Speicherstellen verwendet. Wenn die nicht innerhalb des Datenbereichs des Prozessen liegen → Segfault beim Zugriff.
Benutzeravatar
snafu
User
Beiträge: 5836
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Dienstag 2. Juni 2009, 10:11

Oh, Mann. Es scheitert schon an der Initalisierung von gtk_init() (wieder Segfault). Was übersehe ich denn hier?

Code: Alles auswählen

import ctypes
from ctypes import c_char_p, c_int, POINTER
from ctypes.util import find_library
import sys


argc = len(sys.argv)
argv = (c_char_p * argc)(*sys.argv)

gtk = ctypes.CDLL(find_library('gtk-x11-2.0'))
gtk_init = gtk.gtk_init
gtk_init.argtypes = [c_int, POINTER(c_char_p)]
gtk_init(argc, argv)
Benutzeravatar
Trundle
User
Beiträge: 591
Registriert: Dienstag 3. Juli 2007, 16:45

Dienstag 2. Juni 2009, 10:47

Schau dir doch noch einmal an, welche Signatur `gtk_init()` hat.
"Der Dumme erwartet viel. Der Denkende sagt wenig." ("Herr Keuner" -- Bertolt Brecht)
rayo
User
Beiträge: 773
Registriert: Mittwoch 5. November 2003, 18:06
Wohnort: Schweiz
Kontaktdaten:

Dienstag 2. Juni 2009, 10:54

Funktionierts so?

Code: Alles auswählen

import ctypes
from ctypes import c_char_p, c_int, POINTER, byref
from ctypes.util import find_library
import sys


argc = len(sys.argv)
argv = (c_char_p * argc)(*sys.argv)

gtk = ctypes.CDLL(find_library('gtk-x11-2.0'))
gtk_init = gtk.gtk_init
gtk_init.argtypes = [POINTER(c_int), POINTER(POINTER(c_char_p))]
gtk_init(byref(argc), byref(argv))
BlackJack

Dienstag 2. Juni 2009, 10:56

@snafu: Wie sehen denn Deine C-Kenntnisse und Erfahrung aus? Ohne sollte man IMHO die Finger von `ctypes` lassen.
Benutzeravatar
snafu
User
Beiträge: 5836
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Dienstag 2. Juni 2009, 11:19

@BlackJack:

Meine C-Kenntnisse halten sich so in Grenzen. Ich kann's einigermaßen lesen aber nicht so gut "sprechen". Dazu kommt dann noch das Problem, das ganze in `ctypes` auszudrücken.

Ich habe jetzt bemerkt, dass ich auf den Inhalt des Arrays zeige, aber die Funktion verlangt das Array selbst (sind ja auch 3 Zeiger in der Signatur). Mein Problem ist gerade, welchen Typ ich `POINTER` (oder besser: `POINTER(POINTER)`) mitgeben muss. Einen Typ `Array` finde ich nämlich in der Auflistung nicht. Mag aber auch sein, dass ich's mir gerade schwerer mache als es ist, da C wie gesagt noch relatives Neuland für mich ist.
fred.reichbier
User
Beiträge: 155
Registriert: Freitag 29. Dezember 2006, 18:27

Dienstag 2. Juni 2009, 11:24

Du könntest dir den ctypes-Code auch generieren lassen. Ich hab da mit wraptypes von pyglet gute Erfahrungen gemacht ;)
Benutzeravatar
snafu
User
Beiträge: 5836
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Dienstag 2. Juni 2009, 11:30

rayo hat geschrieben:Funktionierts so?

Code: Alles auswählen

[...]
gtk_init.argtypes = [POINTER(c_int), POINTER(POINTER(c_char_p))]
gtk_init(byref(argc), byref(argv))
Das erste Argument in Zeile 3 kann auf keinen Fall funktionieren. Schließlich ist `argc` ein Python-Integer. Was gehen würde, ist `byref(c_int(argc))`, aber ich weiß nicht, ob man unbedingt über die Referenz gehen muss.

Und der doppelte Zeiger bezieht sich auf das Array (hab ich wie gesagt auch schon bemerkt), du sagst ihm allerdings, dass ein `char*` kommt, was ebenfalls zum Fehler führt. Alle drei Zeiger dürften damit abgedeckt sein, aber man weiß halt nicht den Typ für `POINTER`.
rayo
User
Beiträge: 773
Registriert: Mittwoch 5. November 2003, 18:06
Wohnort: Schweiz
Kontaktdaten:

Dienstag 2. Juni 2009, 11:40

Klar muss man ueber die Referenz gehen:

Code: Alles auswählen

void gtk_init(int *argc, char ***argv);
Also das erste Argument ist ein Pointer auf argc, mittels byref uebergibst du den Pointer.

Das 2. Argument ist ein Pointer auf einen Array von Strings, darum der 3 fache Pointer, Pointer auf Array, Array selbst ist ein Pointer auf das erste Element und das Element selbst ist ein Array of Char. Darum POINTER(POINTER(char_p)).

So jetzt mit argc als c_int. Funktionierts so?

Code: Alles auswählen

import ctypes
from ctypes import c_char_p, c_int, POINTER, byref
from ctypes.util import find_library
import sys


argc = c_int(len(sys.argv))
argv = (c_char_p * argc)(*sys.argv)

gtk = ctypes.CDLL(find_library('gtk-x11-2.0'))
gtk_init = gtk.gtk_init
gtk_init.argtypes = [POINTER(c_int), POINTER(POINTER(c_char_p))]
gtk_init(byref(argc), byref(argv))
[/code]
Benutzeravatar
snafu
User
Beiträge: 5836
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Dienstag 2. Juni 2009, 11:56

@ fred.reichbier:

Der wirft mir schon nen Fehler wenn ich nur das Beispiel aus dem Docstring der __init__.py ausprobieren will.
Antworten