PyObjC: Python Interpreter einbetten?

  • PyObjC: Python Interpreter einbetten?

    Hallo,

    PyObjC dient ja eigentlich dazu, von Python aus Objective-C/Cocoa zu benutzen. Ich habe jetzt aber den umgekehrten Fall: ich habe eine Anwendung in Objective-C und möchte darin einen Python Interpreter einbetten (da einige Teile der Programmlogik in Python geschrieben sind).

    Dies kann ich natürlich mit der Python C-API erreichen (und mache ich im Moment schon). Jetzt bin ich aber auch über die PyObjC-Dokumentation gestolpert und habe gesehen, dass es auch erlaubt auf Python Objekte von Objective-C aus zuzugreifen.

    Leider ist die PyObjC Dokumentation nicht gerade ausführlich und ich konnte nichts finden, was darauf hinweist, wie ich einem Objective-C Programm mit PyObjC einen Interpreter einbetten kann (falls das überhaupt möglich ist) und ob das in meinem Fall überhaupt Sinn machen würde.

    Wenn ich durch die PyObjC-Quellen suche, dann scheint die einzige Möglichkeit, dass ich mit PyObjC ein Plugin schreibe, dass dann den Interpreter einbettet.

    Hat jemand etwas Erfahrung mit PyObjC und kann mir etwas Klarheit verschaffen?

    Rainer
  • RE: PyObjC: Python Interpreter einbetten?

    Original von Tom9811
    Grundsätzlich geht das dem Vernehmen nach. Es gibt hier einen User timnic, der das viel macht. Vielleicht schreibst du ihm einfach eine Mail.


    Danke für den Hinweis.

    Ich hatte auch einen älteren Beitrag von timnic gefunden, in dem er als Möglichkeit dafür die Plugin-Vorgehensweise beschreibt. Aber irgendwie gefällt mir das für meinen Fall nicht (kann ich im Moment nicht genau begründen -- es ist nur so ein Gefühl).

    Ich hätte ganz gern etwas mehr Kontrolle über den eingebetteten Interpreter. Als Plugin ist das ja einfach ein Black-Box (und kann alles sein, d.h. nicht nur Python).

    Ich werde mal timnic kontaktieren und nachfragen, ob die Plugin-Vorgehensweise die einzige Möglichkeit ist.

    Rainer
  • Original von timnic
    was möchtest Du denn genau machen? Ich hab zwar jetzt nicht soooo viel mit PyObjC gearbeitet, aber ein bisschen kann ich sicherlich helfen. Hast Du Dir schon das Beispiel "PyInterpreter" auf pyobjc.sourceforge.net/examples/index.php angeschaut?


    Das Beispiel habe ich mir angeschaut; das ist aber ein Beispiel, das komplett in Python geschrieben ist. Bei mir ist der Fall etwas anders: mein Programm ist in Objective-C geschrieben. Aber einige Teile des Backends möchte ich gerne in Python schreiben. Im Moment recherchiere ich die Möglichkeiten.

    PyObjC hat nach meinem Verständnis zwei Möglichkeiten: man kann mit PyObjC standalone Programme in Python schreiben oder man kann Plugins schreiben, die man dann z.B. in einer "normalen" Cocoa Anwendung laden kann und man kann wohl die Python Objekte von Objective-C aus instantiieren und Funktionen darauf aufrufen.

    Das heißt, dass die Plugin-Methode für meinen Fall funktionieren müsste. Allerdings frage ich mich, ob es noch andere Methoden gibt (d.h. Python von Objective-C aus aufzurufen). Bis jetzt habe ich die Python C-API verwendet; diese ist aber nicht sonderlich komfortabel.

    Rainer
  • Mit diesem Thema habe ich mich auch noch nicht so beschäftigt.

    Warum genau, möchtest Du keinen Plugin generieren? Ist vielleicht nicht die schönste Möglichkeit, funktioniert aber.

    Wenn Du über die Python-C Schnittstelle gehen möchtest, so gibt es da Swig oder Boost-Python, was Dir dir Arbeit erleichtern könnte.

    Wahrscheinlich kann man auch im PyObjC Sourcecode fündig werden, wie man direkt den Python Interpreter aufruft und mit diesem von Cocoa aus kommuniziert. Da habe ich aber noch nie reingeschaut.
  • Original von timnic
    Warum genau, möchtest Du keinen Plugin generieren? Ist vielleicht nicht die schönste Möglichkeit, funktioniert aber.


    Ich habe keinen handfesten Grund dafür, kein Plugin zu verwenden. Es fühlt sich einfach nur seltsam an, einen integralen Bestandteil in ein Plugin auszulagern.

    Ich werde mal am Wochenende (oder wann immer ich mal Zeit finde), ein wenig damit rumspielen und schauen, ob ich die notwendigen Features hinkriege -- ein Teil in Python ist blockierende Netzwerkkommunikation, und die will/muss ich in einen Thread auslagern. Mal schauen, wie ich das mit PyObjC hinkriege (oder ob ich das einfach in einen Python-Thread stecken kann). Schön wäre aber, wenn der ganze Python-Kram in einen eigenen Thread läuft und meine GUI nicht blockiert...

    Rainer
  • Die Einwände bzgl. des Plugins kann ich natürlich verstehen. Da muss es eine bessere Möglichkeit geben.

    Threads jedoch sind sehr einfach in PyObjC zu machen:

    Quellcode

    1. NSThread.detachNewThreadSelector_toTarget_withObject_("myThreadMethod", self, None)

    ruft in Python die Methode myThreadMethod auf. In dieser kann man jetzt alles mögliche tun. (Wie das mit dem Python GIL (Global Interpreter Lock) aussieht, habe ich noch nicht getestet)

    z.B.

    Quellcode

    1. (Die Einzüge stimmen hier nicht überall)
    2. def myThreadMethod(self):
    3. mainpool = NSAutoreleasePool.alloc().init()
    4. x = 0
    5. while 1:
    6. pool = NSAutoreleasePool.alloc().init()
    7. AppHelper.callAfter(self.writeOut, "%d" % x) # könnte sein, dass man hier einen unicode String nehmen muss...
    8. x += 1
    9. del pool
    10. # Das ist natürlich ein BrainDead Beispiel. Sowas sollte man besser über einen NSTimer machen...
    11. time.sleep(1)
    12. del mainpool
    13. def writeOut(self, msg):
    14. ''' performed on main thread'''
    15. self.textField.setStringValue_(msg)
    Alles anzeigen

    Mit den üblichen NSAutoreleasePool Geschichten (dürfte wohl unter Leopard wegfallen). Die Funktion AppHelper.callAfter() führt Code auf dem MainThread aus. Ist wohl sicherer, wenn ich was an die GUI schicken möchte.

    Vielleicht kannst Du ja was damit anfangen.

    Würde mich natürlich interessieren, welchen Weg Du entdeckst, den Python Interpreter an Cocoa anzuflanschen. Also: Bitte Ergebnis mitteilen!

    Tim
  • Hallo,

    mittlerweile habe ich es endlich geschafft, ein bisschen mit PyObjC rumzuspielen (ich hatte ein paar Setup-Probleme, da ich zu viele unterschiedliche Python-Versionen rumliegen habe, aber mit Leopard loest sich das Problem ja von alleine).

    Es gefaellt mir wirklich sehr gut. Mit dem aelteren Posting zu PyObjC im Forum war ich dann in der Lage, in Objective-C Instanzen meiner Python-Objekte aufzurufen. Insgesamt konnte ich einiges an Code wegschmeissen.

    Interessant ist auch, dass Bindings einwandfrei funktionieren. Zum Beispiel habe ich einen NSTreeController, und die Items im Tree sind in Wirklichkeit Python-Objekte!

    Das wirklich sehr cool. Am Anfang hatte ich aber ein paar Probleme. Hier die Sachen, auf die man achten sollte:

    • Python-Objekte, auf die man von Objective-C aus zugreifen (oder erzeugen will) sollten von NSObject ableiten.

    • Die init() Funktion sollte dem folgenden Pattern folgen:

      Quellcode

      1. class MyClass(NSObject):
      2. def init(self):
      3. """
      4. Designated initializer for MyClass
      5. """
      6. # ALWAYS call the super's designated initializer.
      7. # Also, make sure to re-bind "self" just in case it
      8. # returns something else, or even None!
      9. self = super(MyClass, self).init()
      10. if self is None: return None
      11. self.myVariable = 10
      12. # Unlike Python's __init__, initializers MUST return self,
      13. # because they are allowed to return any object!
      14. return self
      Alles anzeigen

    • And das Class-Objekt, das zu der Python-Klasse gehoert kommt man ran (nachdem man das Plugin geladen hat) und man kann eine Instanz davon erzeugen:

      Quellcode

      1. Class MyClass = [bundle classNamed: @"MyClass"];
      2. id myObj = [[MyClass alloc] init];



    Original von timnic
    Threads jedoch sind sehr einfach in PyObjC zu machen:

    Quellcode

    1. NSThread.detachNewThreadSelector_toTarget_withObject_("myThreadMethod", self, None)

    ruft in Python die Methode myThreadMethod auf. In dieser kann man jetzt alles mögliche tun. (Wie das mit dem Python GIL (Global Interpreter Lock) aussieht, habe ich noch nicht getestet)


    Zu den Threads bin ich dann leider noch nicht gekommen. Meine urspruengliche Idee war eigentlich, das PyObjC-Plugin in einem eigenen Thread zu laden, so dass der Python-Interpreter in einem eigenen Thread laeuft (keine Ahnung, ob das funktioniert).

    Allerdings bin ich mittlerweile davon abgekommen, nachdem ich gesehen habe, dass es sehr einfach ist, die PyObjC-Objekte direkt in Bindings zu verwenden.

    Allerdings frage ich mich im Moment, ob es besser ist, den Thread in Python mittels NSThread zu erzeugen (so wie Du es zeigst), oder aber ob es besser ist, Python-Threads zu verwenden.

    Die Geschichte mit dem Python GIL habe ich nicht so ganz verstanden (habe die Python Dokumentation gelesen), allerdings glaube ich schon, dass das in PyObjC funktioniert, da ich im Source-Code einige gestellen gesehen habe, die sich um den GIL kuemmern.

    Ich werde auf jeden Fall noch einmal ein Update zu meinen Erfahrungen schreiben, wenn ich zu dem Threading-Teil komme.

    Rainer
  • Ich bin mal gespannt, was Du herausbekommst über die Vor/Nachteile von NSThreads gegenüber puren Python Threads. Vielleicht gibt es aufgrund der PyObjC Implementierung überhaupt keinen Unterschied.
    Der GIL verhindert --soweit ich weiß-- dass der Python Interpreter/VM selbst beim Ausführen der Bytecodes in verschiedenen Threads gleichzeitig läuft. Was das genau bedeutet, kann ich nicht sagen -- ich bin nur dummer Python User ;) Jedenfalls gab es wohl große Diskussionen in der Python Gemeinde, wie man den GIL loswerden könnte, um demnächst Multikern CPUs besser unterstützen zu können. Scheint aber wohl nicht so einfach zu sein.