Einschränkungen von PyTuple_SetItem

8

Ich habe ein Python-Erweiterungsmodul, das ein Tupel als Attribut eines anderen Objekts erstellt und Elemente im Tupel setzt. Immer wenn ich dieses Modul in Python ausführe, erhalte ich den Fehler SystemError: bad argument to internal function

Nachdem ich die Dokumente für PyTuple gelesen und mein Programm ein paar Stunden lang getestet hatte, konnte ich immer noch nicht herausfinden, was zur Hölle los war. Das Ausführen meines Programms durch einen Debugger zeigte an, dass das Problem innerhalb eines Bibliotheksaufrufs innerhalb des Python-Interpreters aufgetreten ist. So, schließlich habe ich mir den Python-Quellcode angeschaut, und endlich habe ich das Problem erkannt. Die Funktion PyTuple_SetItem hat eine interessante Einschränkung, von der ich nichts wusste, und kann sie nicht explizit finden.

Hier ist die wichtige Funktion in der Python-Quelle (zur Verdeutlichung bearbeitet):

%Vor%

Die wichtige Zeile hier ist die Bedingung op- & gt; ob_refcnt! = 1 . Hier ist das Problem: Sie können nicht einmal PyTuple_SetItem aufrufen, es sei denn, das Tuple hat einen ref-count von 1. Es sieht so aus, als ob Sie nie PyTuple_SetItem verwenden sollten, außer direkt nachdem Sie ein Tupel erstellt haben mit PyTuple_New() . Ich denke, das macht Sinn, da Tupel ja eigentlich unveränderlich sein sollten, also hilft diese Einschränkung, Ihren C-Code besser mit den Abstraktionen des Python-Typ-Systems in Einklang zu bringen.

Allerdings kann ich diese Einschränkung nirgendwo dokumentiert finden. Relevante Dokumente scheinen hier und hier , von denen keine diese Einschränkung angibt. Die Dokumente sagen im Grunde, dass beim Aufruf von PyTuple_New(X) alle Elemente im Tupel auf NULL initialisiert werden. Da NULL kein gültiger Python-Wert ist, ist es Aufgabe des Erweiterungsmodul-Programmierers sicherzustellen, dass alle Steckplätze im Tupel mit korrekten Python-Werten gefüllt sind, bevor das Tupel an den Interpreter zurückgegeben wird. Aber es sagt nirgendwo, dass dies getan werden muss, während das Tuple-Objekt eine Referenzzählung von 1 hat.

Nun, das Problem ist, dass ich mich im Grunde in eine Ecke eingeordnet habe, weil ich mir dieser (undokumentierten?) Beschränkung auf PyTuple_SetItem nicht bewusst war. Mein Code ist so strukturiert, dass es sehr unpraktisch ist, Elemente in das Tupel einzufügen, bis nachdem das Tupel selbst zu einem Attribut eines anderen Objekts geworden ist. Wenn es also Zeit ist, die Elemente im Tupel zu füllen, hat das Tupel bereits eine höhere Ref-Zahl.

Ich werde wahrscheinlich meinen Code umstrukturieren müssen, aber ich habe ernsthaft darüber nachgedacht, den ref count auf dem Tuple nur vorübergehend auf 1 zu setzen, die Items einzufügen und dann die ursprüngliche ref count wiederherzustellen. Natürlich ist das ein schrecklicher Hack, ich weiß, und keine dauerhafte Lösung. Unabhängig davon würde ich gerne wissen, ob die Anforderungen bezüglich der Refs auf dem Tuple irgendwo dokumentiert sind. Ist es nur ein Implementierungsdetail von CPython oder ist es etwas, auf das sich API-Benutzer als erwartetes Verhalten verlassen können?

    
Channel72 24.05.2011, 14:16
quelle

2 Antworten

7

Ich bin mir ziemlich sicher, dass Sie die Einschränkungen umgehen können, indem Sie PyTuple_SET_ITEM anstelle von PyTuple_SetItem verwenden. PyTuple_SET_ITEM ist ein in tupleobject.h definiertes Makro wie folgt:

%Vor%

Also, wenn Sie absolut, definitiv und absolut sicher sind:

  1. op ist ein Tupel-Objekt
  2. Sie haben den Slot i im Tupel bisher noch nicht initialisiert
  3. Sie besitzen einen Verweis auf v und möchten, dass das Tupel es stehlen und
  4. es gibt keine Chance, dass ein anderes Python-Objekt das Tupel für irgendetwas verwendet, bevor Sie PyTuple_SET_ITEM aufrufen

Dann sind Sie sicher, dass Sie PyTuple_SET_ITEM verwenden können.

    
Tamás 24.05.2011, 14:20
quelle
2

Die Python-C-API ist sehr undokumentiert, und ich wäre nicht überrascht, wenn diese Einschränkung nirgends erwähnt würde .

Natürlich solltest du Tupel niemals modifizieren, wenn sie schon einmal etwas erreicht haben; Übergeben Sie entweder die Elemente, die Sie in das Tupel einfügen müssen, oder verwenden Sie stattdessen eine Liste.

    
Ignacio Vazquez-Abrams 24.05.2011 14:19
quelle

Tags und Links