Jump to content
iqrf

How to use PyArg_ParseTupleAndKeywords

Recommended Posts

Hi,
I have a TPythonType object TPyDevice = class(TPyObject) and Interface methods in it
function DoSendAndReceive(args: PPyObject): PPyObject; cdecl;

function TPyDevice.DoSendAndReceive(args: PPyObject): PPyObject; cdecl;
var
  data: TBytes;
  pyBytes: PPyObject;
  timeout: Single;
  logging: Boolean;

begin
  with GetPythonEngine do begin
    Adjust(@Self);

    logging := False;

    if PyArg_ParseTuple( args, 'S|fp:device.send_and_receive', @pyBytes, @timeout, @logging) <> 0 then begin
      data := PyBytesAsBytes(pyBytes);
      var response := False;
       ....
       ....

      if response then begin
        pyBytes := PyBytes_FromStringAndSize(PAnsiChar(data), Length(data));
        Result := pyBytes;
      end
      else Result := ReturnNone;

    end
    else
      Result := nil;
  end;
end;

I use it like this 

response = device_manager.device.send_and_receive(dat, 0, True) or 
response = device_manager.device.send_and_receive(dat)
 

I would like to call the function like this

device_manager.device.send_and_receive(data=dat, timeout 0, logging=True) or
device_manager.device.send_and_receive(data=dat,  logging=True)

I edited it like this

function TPyDevice.DoSendAndReceive(args, kwds: PPyObject): PPyObject; cdecl;
var
  data: TBytes;
  pyBytes: PPyObject;
  timeout: Single;
  logging: Boolean;
  keyArray: array of AnsiString;
  keyPointerArray: array of PAnsiChar;


begin
  with GetPythonEngine do begin
    Adjust(@Self);

    logging := False;
    KeyArray := ['data', 'timeout', 'logging'];
    KeyPointerArray := [PAnsiChar(KeyArray[0]), PAnsiChar(KeyArray[1]), PAnsiChar(KeyArray[2])];
    if PyArg_ParseTupleAndKeywords(args, kwds, 'S|fp:device.send_and_receive', @keyPointerArray[0], @pyBytes, @timeout, @logging) <> 0 then begin
      data := PyBytesAsBytes(pyBytes);
      var response := False;
      ....
      ....

      if response then begin
        pyBytes := PyBytes_FromStringAndSize(PAnsiChar(data), Length(data));
        Result := pyBytes;
      end
      else Result := ReturnNone;
    end
    else
      Result := nil;
  end;
end;

TPyDevice = class(TPyObject)
  ...
  constructor CreateWith( PythonType : TPythonType; args : PPyObject ); override;
    // Basic services
  function  Repr : PPyObject; override;
  function  Str : PPyObject; override;
  function  GetAttr(Key: PAnsiChar): PPyObject; override;
  function  SetAttr(Key: PAnsiChar; Value: PPyObject): Integer; override;
  
    // Methods of TPyDevice
  ...
    // Class methods
  class procedure RegisterMethods(PythonType: TPythonType); override;
  class procedure RegisterMembers(PythonType: TPythonType); override;
  class procedure RegisterGetSets(PythonType: TPythonType); override;

     // Interface methods
  function DoSendAndReceive(args, kwds: PPyObject): PPyObject; cdecl;
   ...
end;

class procedure TPyDevice.RegisterMethods(PythonType: TPythonType);
begin
  inherited;
  with PythonType do begin
      AddMethod('send_and_receive', @TPyDevice.DoSendAndReceive, 'device_manager.device.send_and_recive(data: bytes)');
    ....
    end;
end;


  FPythonType_Device := TPythonType.Create(nil);
  FPythonType_Device.Name := 'FPythonType_Device';
  FPythonType_Device.GenerateCreateFunction := False;
  FPythonType_Device.Engine := FEngine;
  FPythonType_Device.OnInitialization := OnInitialization_PythonType_Device;
  FPythonType_Device.TypeName := 'Device';
  FPythonType_Device.Prefix := '';
  FPythonType_Device.Services.Basic := [bsGetAttr, bsSetAttr, bsRepr, bsStr];
  FPythonType_Device.Module := FPythonModule_DeviceManager;
  
  pDevice := FPythonType_Device.CreateInstance;
  FEngine.CheckError;

  device := TPyDevice(PythonToDelphi(pDevice));
  FPythonModule_DeviceManager.SetVar('device', pDevice);
  FEngine.Py_DecRef(pDevice);

But I get TypeError: send_and_receive() takes no keyword arguments. Execution of the send_and_receive function will not start at all. I looked at the Demo32 example, but there it is used with CreateWith which has parameters args, kwds: PPyObject already PythoEngine.pas

Thank you in advance for your help.

Edited by iqrf

Share this post


Link to post

Yes, according to Demo32 I implemented it. But it is an example of use in CreateWidth. I am trying to use it in a method.

Share this post


Link to post

I will answer myself. To register a method, you need to use AddMethodWithKeywords instead of AddMethod. It was enough to look at WrapDelphi.pas. I think it would be beneficial if Demo32 included an example of such a method.

14 hours ago, iqrf said:

AddMethod('send_and_receive', @TPyDevice.DoSendAndReceive, 'device_manager.device.send_and_recive(data: bytes)');

and still to fix

Quote

KeyPointerArray := [PAnsiChar(KeyArray[0]), PAnsiChar(KeyArray[1]), PAnsiChar(KeyArray[2])];

KeyPointerArray := [PAnsiChar(KeyArray[0]), PAnsiChar(KeyArray[1]), PAnsiChar(KeyArray[2]), nil];

Edited by iqrf

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

×