filippocurati
filippocurati

Reputation: 45

Python4Delphi - Error in wrapping delphi interface with TPyDelphiWrapper.WrapInterface

I am using Python 3.8 with Delphi 10.4.2.

I am trying to use the components of Python4Delphi to access through a Python script some interfaces defined in Delphi.

At design-time I added the TPythonEngine, TPythonModule and TPyDelphiWrapper components to my project's VCL form.

So I defined 3 interfaces, implemented by 3 classes respectively, as below

type
  IPerson = interface (IUnknown)
    ['{1D21B5B6-25DE-4884-8BDB-8E2D9A239D64}']
    function GetName : string;
    procedure SetName ( value : string );
    property Name: string read GetName write SetName;   
    function GetSurname: string;
    procedure SetSurname(value : string);
    property Surname : string read GetSurname write SetSurname;    
    function GetInfo : string;
  end;

  ICustomer = interface (IPerson)
    ['{8742364C-33E8-4FF4-86FB-C19AF67A735B}']
    function GetCustomerNumber : string;
    procedure SetCustomerNumber ( value : string );
    property CustomerNumber : string read GetCustomerNumber write SetCustomerNumber;
  end;

  ISupplier = interface ( IPerson )
    ['{420FFF78-92DE-4D7E-9958-FDA95748EEB7}']
    function GetSupplierNumber : string;
    procedure SetSupplierNumber ( value : string );
    property SupplierNumber : string read GetSupplierNumber write SetSupplierNumber;
  end;

  TPerson = class ( TInterfacedObject , IPerson)
  private
    FName : string;
    FSurname : string;    
    function GetName : string;
    procedure SetName ( value : string );    
    function GetSurname: string;
    procedure SetSurname(value : string);

  public
    property Surname : string read GetSurname write SetSurname;
    property Name: string read GetName write SetName;    
    function GetInfo : string; virtual;
  end;


  TCustomer = class ( TPerson , ICustomer)
  private
    FCustomerNumber : string;
    function GetCustomerNumber : string;
    procedure SetCustomerNumber ( value : string);

  public    
    property CustomerNumber : string read GetCustomerNumber write SetCustomerNumber;
    function GetInfo: string; override;
  end;


  TSupplier = class ( TPerson , ISupplier)
  private
    FSupplierNumber : string;
    function GetSupplierNumber : string;
    procedure SetSupplierNumber ( value : string );

  public
    property SupplierNumber : string read GetSupplierNumber write SetSupplierNumber;
    function GetInfo : string; override;
  end;

In the Create method of the form, I defined 3 variables, one for each of the 3 interfaces, and through the PyDelphiWrapper I passed them to the Python module in 3 different Python variable.

procedure TFrmTestInterface.FormCreate(Sender: TObject);
var
  LPerson : IPerson;
  LCustomer : ICustomer;
  LSupplier : ISupplier;
  Py: PPyObject;
begin

  LPerson := TPerson.Create;
  LCustomer := TCustomer.Create;
  LSupplier := TSupplier.Create;

  LPerson.Name := 'Pippo';
  LPerson.Surname := 'Rossi';

  LCustomer.Name := 'Pluto';
  LCustomer.Surname := 'Verdi';

  LSupplier.Name := 'Paperino';
  LSupplier.Surname := 'Bianchi';


  Py := PyDelphiWrapper1.WrapInterface(TValue.From(LPerson));
  PythonModule1.SetVar('delphi_person', Py);
  GetPythonEngine.Py_DecRef(Py);

  Py := PyDelphiWrapper1.WrapInterface(TValue.From(LCustomer));
  PythonModule1.SetVar('delphi_customer', Py);
  GetPythonEngine.Py_DecRef(Py);

  Py := PyDelphiWrapper1.WrapInterface(TValue.From(LSupplier));
  PythonModule1.SetVar('delphi_supplier', Py);
  GetPythonEngine.Py_DecRef(Py);

end;

At runtime the variables are correctly interpreted, but every time I try to access one of the properties defined in the interface I always get the same error.

This is the Python script I try to run:

from delphi_module import delphi_person, delphi_customer, delphi_supplier 

print('type(delphi_person) = ', type(delphi_person)) 
print('type(delphi_customer) = ', type(delphi_customer)) 
print('type(delphi_supplier) = ', type(delphi_supplier)) 

print(delphi_person.Name)

And the error I get

Traceback (most recent call last): File "", line 7, in AttributeError: Error in getting property "Name". Error: Unknown attribute

The type(...) command runs correctly for the three variables.

If instead of using 3 variables of the interface type, I declare each variable as a class type, using the PyDelphiWrapper.Wrap method, everything works correctly!

procedure TFrmTestInterface.FormCreate(Sender: TObject);
var
  LPerson : IPerson;
  LCustomer : ICustomer;
  LSupplier : ISupplier;
  Py: PPyObject;
begin

  LPerson := TPerson.Create;
  LCustomer := TCustomer.Create;
  LSupplier := TSupplier.Create;

  LPerson.Name := 'Pippo';
  LPerson.Surname := 'Rossi';

  LCustomer.Name := 'Pluto';
  LCustomer.Surname := 'Verdi';

  LSupplier.Name := 'Paperino';
  LSupplier.Surname := 'Grandi';


  Py := PyDelphiWrapper1.Wrap(LPerson, TObjectOwnership.soReference);
  PythonModule1.SetVar('delphi_person', py);
  GetPythonEngine.Py_DECREF(py);

  Py := PyDelphiWrapper1.Wrap(LCustomer, TObjectOwnership.soReference);
  PythonModule1.SetVar('delphi_customer', py);
  GetPythonEngine.Py_DECREF(py);

  Py := PyDelphiWrapper1.Wrap(LSupplier, TObjectOwnership.soReference);
  PythonModule1.SetVar('delphi_supplier', py);
  GetPythonEngine.Py_DECREF(py);

end;

With the same Python script I get the correct output without errors

enter image description here

Anyone have any idea what I'm doing wrong with using the TPyDelphiWrapper to wrap interface type variables for Python scripts?

Upvotes: 1

Views: 622

Answers (1)

Kees Vermeulen
Kees Vermeulen

Reputation: 46

Delphi does not add RTTI on properties defined in interfaces. Therefore property 'Name' cannot be found by the Python engine. RTTI is available for the methods when you add {$M+} before the interface declaration. Calling delphi_person.GetName() should work then.

There is another issue with using interfaces and the Python engine, the interface is not locked when you call WrapInterface. Therefore the object will be released when it goes out of scope.

Upvotes: 1

Related Questions