Python for Delphi problem

Greg Chapman glc at well.com
Tue Feb 11 10:51:31 EST 2003


You have to be more careful about your refcounting.  

>procedure TForm1.Button1Click(Sender: TObject);
>var
>  pyName, pyModule, pyDict, pyList, pyItem : PPyObject;
>  Index, pyListSize : Integer;
>  Item, TempItem : PChar;
>begin
>  {import moddule}
>  pyName := PythonEngine1.PyString_FromString( PChar(Edit1.Text) );
>  pyModule := PythonEngine1.PyImport_Import(pyName);
>
>  if pyModule <> nil then
>  begin
>    {Get the modules dictionary}
>    pyDict := PythonEngine1.PyModule_GetDict(pyModule);
>    {Get the keys list from the dictionary}
>    pyList := PythonEngine1.PyDict_Keys(pyDict);
>    {Get the size of the list}
>    pyListSize := PythonEngine1.PyList_Size(pyList);
>
>    {Allocate space for a string to hold the results}
>    Item := StrAlloc(21);
     You dont't need to do this; it would be much better to declare Item as a
string (AnsiString); Delphi automatically converts null-terminated PChars to
strings when assigned.  Python ensures that the char * buffer is
null-terminated.
>
>    for Index := 0 to pyListSize do
>    begin
>      {Get each item in the list}
>      pyItem := PythonEngine1.PyList_GetItem(pyList, Index);
>      {Convert the item to a null terminated string}
>      TempItem := PythonEngine1.PyString_AsString(pyItem);
>      {Copy to the new Item string}
>      StrLCopy(Item, TempItem, 20);
>      PythonEngine1.Py_DECREF(pyItem);
>      pyItem := nil;
>      ListBox1.Items.Add(String(Item));
>      PythonEngine1.Py_DECREF(pyList);
       ^^^ the above is the same as freeing the list -- you don't want to do   
           this in the middle of a loop which is accessing it!
>    end;
>    PythonEngine1.Py_DECREF(pyModule);
>  end;
>    PythonEngine1.Py_DECREF(pyName);
>end;
>

In general, I'd introduce some try finally blocks to ensure that references are
properly DECREF'd.  I assume the PythonEngine supports the equivalent of
Py_XDECREF (which checks for a NULL pointer before DECREFing).  If so, you could
change the above to:

procedure TForm1.Button1Click(Sender: TObject);
var
  pyName, pyModule, pyDict, pyList, pyItem : PPyObject;
  Index, pyListSize : Integer;
  TempItem: PChar;
begin
  pyModule:= nil; pyList:= nil;
  pyName := PythonEngine1.PyString_FromString( PChar(Edit1.Text) );
  if pyName = nil then 
    Exit; {raise exception}
  try
    {import moddule}
    pyModule := PythonEngine1.PyImport_Import(pyName);
    if pyModule = nil then
      Exit;    {raise exception}

    {Get the module's dictionary}
    pyDict := PythonEngine1.PyModule_GetDict(pyModule);
    {Get the keys list from the dictionary}
    pyList := PythonEngine1.PyDict_Keys(pyDict);
    {Get the size of the list}
    pyListSize := PythonEngine1.PyList_Size(pyList);

    for Index := 0 to pyListSize-1 do
    begin
      {Get each item in the list}
      pyItem := PythonEngine1.PyList_GetItem(pyList, Index);
      if pyItem = nil then
          Break;     {raise exception}
      try
        TempItem:= PyString_AsString(pyItem);
        if TempItem = nil then
          Break;     {raise exception}

        {use implicit conversion of PChar to string}
        ListBox1.Items.Add(TempItem);

      finally
        PythonEngine1.Py_DECREF(pyItem);
      end;
    end;
  finally
    PythonEngine1.Py_XDECREF(pyList);
    PythonEngine1.Py_XDECREF(pyModule);
    PythonEngine1.Py_DECREF(pyName);
  end;
end;

---
Greg Chapman





More information about the Python-list mailing list