Jump to content
Sign in to follow this  
marcelsema

Correctly let python free a result

Recommended Posts

Hello everyone,

I am looking to destroy an object once it is no longer referenced in python.


I am struggeling to correctly do this.
 

unit Unit1;



interface

uses
  SysUtils, Classes,
  Windows, Messages, Graphics, Controls, Forms, Dialogs,
  StdCtrls, ExtCtrls, ComCtrls,
  PythonEngine, Vcl.PythonGUIInputOutput, WrapDelphi;

type
  TForm1 = class(TForm)
    Splitter1: TSplitter;
    Memo1: TMemo;
    PythonEngine1: TPythonEngine;
    PythonModule1: TPythonModule;
    Panel1: TPanel;
    Button1: TButton;
    PythonGUIInputOutput1: TPythonGUIInputOutput;
    Memo2: TMemo;
    PyDelphiWrapper1: TPyDelphiWrapper;


    procedure Button1Click(Sender: TObject);
  private
  public
    destructor Destroy; override;
    function GetDelphiWrapper: TPyDelphiWrapper;
    function GetPythonEngine: TPythonEngine;
  end;

  TEntry = class(TObject)
  strict private
    FstrName: string;
    FclsStringList: TStringList;
  public
    constructor Create(strName: string);
    destructor Destory;
    function Name: string;
  end;

  TManager = class(TObject)
  public
    function GetNewEntry: PPyObject;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}



procedure TForm1.Button1Click(Sender: TObject);
var
  clsManager: TManager;
  pyManager: PPyObject;
begin
  clsManager := TManager.Create;
  pyManager := PyDelphiWrapper1.Wrap(clsManager, soReference);
  PythonModule1.SetVar( 'manager', pyManager );
  PythonEngine1.ExecStrings( memo1.Lines );
  PythonEngine1.Py_DecRef(pyManager);
end;


{ TManager }

function TManager.GetNewEntry: PPyObject;
begin
  // I want TEntry to be destroyed by python once it is no longer used
  Result := Form1.GetDelphiWrapper.Wrap(TEntry.Create('Test'), soOwned);
  Form1.GetPythonEngine.Py_DECREF(Result);
end;

destructor TForm1.Destroy;
begin
  FreeAndNil(PythonModule1);
  FreeAndNil(PythonEngine1);
   inherited;
end;


function TForm1.GetDelphiWrapper: TPyDelphiWrapper;
begin
  Result := PyDelphiWrapper1;
end;

function TForm1.GetPythonEngine: TPythonEngine;
begin
  Result := PythonEngine1;
end;

{ TEntry }

constructor TEntry.Create(strName: string);
begin
  inherited Create;
  FclsStringList := TStringList.Create;
  FstrName := strName;
end;

destructor TEntry.Destory;
begin
  FreeAndNil(FclsStringList);
  inherited;
end;

function TEntry.Name: string;
begin
  Result := FstrName;
end;

end.


 

from spam import *

entry = manager.GetNewEntry()
print(entry.Name())

 

 

After clicking the button, I get an exception accessing an invalid pointer.
 

I also tried returning the object directly instead of wraping it to a PPyObject, but that did not work.

 

Thank you for your help.

Best regards,

Marcel

Share this post


Link to post
function TManager.GetNewEntry: PPyObject; begin
  // I want TEntry to be destroyed by python once it is no longer used
  Result := Form1.GetDelphiWrapper.Wrap(TEntry.Create('Test'), soOwned);
  Form1.GetPythonEngine.Py_DECREF(Result);
end;

In the code above Py_DECREF will destroy the TEntry instance immediately,   You can confirm that by debugging and putting a brekpoint at TEntry.Destroy.
Just remove the Py_DECREF statement.

Explanation.: Wrap returns a python object with reference count of 1.  When you decrease the reference count, it goes down to 0, and because the wrapped Delphi object is owned it gets destroyed 
 

The difference with

clsManager := TManager.Create;
pyManager := PyDelphiWrapper1.Wrap(clsManager, soReference);
PythonModule1.SetVar( 'manager', pyManager );
PythonEngine1.ExecStrings( memo1.Lines );
PythonEngine1.Py_DecRef(pyManager);

is that you call SetVar which increases the reference count.  However the above code leaks clsManager.   It does not get destroyed because you  set the Ownership to soReference.    Either change that or call clsManager.Free at the end.

 

Conclusion:   Python reference counting can be tricky. You should try to understand the rules behind reference counting. (e.g. Reference Counting in Python (tripod.com)) if you use the Python API directly.   High level P4D modules (WrapDelphi and VarPython) to a large extent remove that need and ensure correct use of reference counting.

Edited by pyscripter

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

×