Python C-API: Modul im Arbeitsverzeichnis laden?

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.
Antworten
Dackel
User
Beiträge: 11
Registriert: Mittwoch 4. März 2009, 20:44

Hallo allerseits,

ich habe ein simples Problem: Ich habe ein C++-Programm, in dem ich mit Python-Code interagieren will, genauergesagt:
  • Ich will im C++-Programm ein Python-Modul im Arbeitsverzeichnis laden.
  • Ein Objekt aus einer in diesem Modul definierten Klasse instanziieren und einige Members setzen.
  • Eine Methode des Objektes ausführen und das Objekt danach entsorgen.
Ich habe bis jetzt direkt die Python C-API und boost:python versucht und scheitere bei beiden am gleichen Problem: Das importieren des Moduls, welches im Arbeitsverzeichnis liegt schlägt fehl mit dem ImportError, dass das Modul nicht existieren würde. Meine erste Idee war, dass vielleicht von Py_Initialize() PYTHONHOME nicht richtig exportiert wird und ich habe versucht, da mit putenv() noch das Arbeitsverzeichnis hinzuzufügen aber ohne Erfolg.
Eigentlich ist bis jetzt alles, was ich tue (reduziert auf das Nötigste):

Code: Alles auswählen

...includes...
using namespace boost::python;

void meineCFunktion() {
...
  Py_Initialize();
  object foobar = import("foobarModule");
...
}
Ich bin mit der C-API, bzw. boost da noch nicht wirklich firm und 90% der Beispiele, die man so ergooglet beschäftigen sich leider nicht mit dem Einbetten von Python sondern lediglich mit der umgekehrten Variante, Python-Module in C zu schreiben :(
Ich bin dankbar für jeden Hinweis.
Benutzeravatar
HWK
User
Beiträge: 1295
Registriert: Mittwoch 7. Juni 2006, 20:44

Hier solltest Du ein paar Anregungen finden.
MfG
HWK
Dackel
User
Beiträge: 11
Registriert: Mittwoch 4. März 2009, 20:44

Danke, aber so wirklich hilft mir das leider nicht :( Ich habe das erste Problem mittlerweile in den Griff gekriegt (hätte putenv() VOR Py_Initialize() setzen sollen), habe jetzt aber immernoch das Problem, dass eigentlich alles, was über ein einfaches print "Hello World" hinausgeht in einer Exception seitens Python endet in einem TypeError endet. Als Beispiel Folgendes:

Code: Alles auswählen

using namespace boost::python;
...
try{
  object main_module = import("__main__");
  object main_namespace == main_module.attr("__dict__");
  object os_module = import("os");
  object ignore = exec("print os.getcwd()\n", main_namespace);
}
...
Dieser Code wirft mir einen "TypeError: 'NoneType' object is unsubscriptable" mit dem ich ehrlich gesagt nix anfangen kann. Mein Verdacht ist, dass das zusätzlich importierte Modul "os" vom Interpreter nicht behalten wird, denn wenn ich versuche, den gleichen Ausdruck per PyRun_SimpleString() auszuführen, bekomme ich einen NameError, dass "os" undefiniert sei.
Benutzeravatar
HWK
User
Beiträge: 1295
Registriert: Mittwoch 7. Juni 2006, 20:44

Hast Du die Importe denn mal so probiert wie in dem erwähnten Beispiel-Script?
MfG
HWK
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

Magst Du uns auch etwas mehr Code zeigen - vielleicht ein Minimalbeispiel in einem Pasteservice?

Und wie compilierst Du Code, der auf boost zurückgreift? bjam?

Gruß,
Christian
Dackel
User
Beiträge: 11
Registriert: Mittwoch 4. März 2009, 20:44

HWK: Die Varianten mit PyRun_SimpleString("import...") und PyImport_Import() hatte ich ganz am Anfang schonmal erfolglos probiert, bevor ich auf boost.python umgestiegen bin. Für das Problem mit dem "NoneType" hab' ich mal kurz ein Testprogramm geschrieben: http://pastebin.com/m4605fe6b

Ich kompiliere und linke das Ganze klassisch mit dem gcc auf der bash.
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

Was passiert, wenn Du auf

Code: Alles auswählen

#include <Python.h>
verzichtest? Siehe hier
Keine Ahnung, ob es hilft - tut mir leid, aber Embedding von C++ mit boost habe ich noch nicht gemacht.

Gruß,
Christian
Benutzeravatar
Trundle
User
Beiträge: 591
Registriert: Dienstag 3. Juli 2007, 16:45

boost::python::exec nimmt drei Argumente entgegen: Den String mit dem Code, ein ``globals``-Objekt und ein ``locals``-Objekt. Du übergibst jedoch nur ein ``globals``-Objekt, also funktionieren keine Zuweisungen (von globalen Namen einmal abgesehen), da das ``locals``-Objekt eben `None` ist und sich folglich nicht wie ein Dict verhält. Übergibt man ein ``locals``-Objekt (einfach noch einmal ``main_namespace``), funktioniert der Code (wenn man den Import von "os" korrigiert).
"Der Dumme erwartet viel. Der Denkende sagt wenig." ("Herr Keuner" -- Bertolt Brecht)
Benutzeravatar
HWK
User
Beiträge: 1295
Registriert: Mittwoch 7. Juni 2006, 20:44

Dackel hat geschrieben:HWK: Die Varianten mit PyRun_SimpleString("import...") und PyImport_Import() hatte ich ganz am Anfang schonmal erfolglos probiert, bevor ich auf boost.python umgestiegen bin. Für das Problem mit dem "NoneType" hab' ich mal kurz ein Testprogramm geschrieben: http://pastebin.com/m4605fe6b

Ich kompiliere und linke das Ganze klassisch mit dem gcc auf der bash.
Wenn ich wie im genannten Thread importiere, funktioniert alles problemlos:

Code: Alles auswählen

// File Test.c

#include <Python.h>
#include <stdio.h>

int main(int argc, char** argv)
{
    PyObject *module, *name;
    Py_Initialize();
    PyRun_SimpleString("import sys");
    PyRun_SimpleString("sys.path.insert(0, '')");
    name = PyString_FromString("MyModule");
    module = PyImport_Import(name);
    printf("MyModule wurde importiert.\n");
    Py_DECREF(name);
    Py_DECREF(module);
    Py_Finalize();
    return 0;
}

Code: Alles auswählen

# File MyModule.py

print 'Das ist das Modul "MyModule"'

Code: Alles auswählen

C:\Programme\Python26\Scripts\cTest>Test
Das ist das Modul "MyModule"
MyModule wurde importiert.
MfG
HWK
Dackel
User
Beiträge: 11
Registriert: Mittwoch 4. März 2009, 20:44

Trundle: Vielen Dank! Damit und der Änderung des Imports zurück auf PyRun_SimpleString() klappt's endlich.
Lässt mich aber fragen: Was bedeuten denn globals und locals in dem Zusammenhang? Ich hatte einfach assoziiert:
  • Gobals = globale Variablen innerhalb des Skripts
  • Locals = lokaler Gültigkeitsbereich des ausgeführten Codeblocks
Dementsprechend hatte ich locals für überflüssig erachtet, es sei denn man nutzt Closures.
Edit: HWK, hmm... vielleicht hatte ich da auch schon einen gewissen Grad an Kuddelmuddel in meinem Code und hab' mir woanders ein Bein gestellt.? :oops: Jetzt wirft das Ganze wenigstens keine Exception mehr.
Mein Code ist jetzt ein furchtbarer Mischmasch aus Python-API und Boost, aber schön war der Ansatz von Anfang an nicht. Aber Chef will's halt so ;)
Benutzeravatar
Trundle
User
Beiträge: 591
Registriert: Dienstag 3. Juli 2007, 16:45

Das sind eben einfach Mappings für die lokalen und globalen Variablen. Im Fall von Code auf Modulebene sind die lokalen Variablen gleichzeitig die globalen Variablen. Aber `exec()` weiß ja nicht, was es für Code ausführt, folglich ist etwas wie ``spam = 'eggs'`` für `exec()` immer eine lokale Zuweisung, wenn kein ``global spam`` vorausging. Also musst du einfach zwei mal das Moduldict übergeben.
"Der Dumme erwartet viel. Der Denkende sagt wenig." ("Herr Keuner" -- Bertolt Brecht)
Antworten