Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

How do I free objects after the script execution? #455

Answered by MKostitsyn
MKostitsyn asked this question in Q&A
Discussion options

I have set of scripts that produce and use objects like this:

from pc1 import Pc as pc
rb = pc.ReceiptBuilder()
# work with receipt builder
rb = None

If I comment the last line, rb seems not to be released automatically.
How can this be avoided?
I would like not to have zombie-objects before I run next script.
I also don't like to be forced to release these objects by hands.

You must be logged in to vote

The following code solves my issue. I hope it will be useful for someone else.
fPyDelphiObjects - is a TDictionary<string, PPyObject>, where I keep all the P4D objects, that were created during script execution.
So this code is kinda my own garbage collector.

I would also appreciate to receive any comments on this method, if it has mistakes or vulnerables.

procedure TDeepyPythonModule.ReleasePyObjects;
begin
  var aScript := TStringList.Create;
  try
    var aGlobals := GetPythonEngine.EvalString('globals()');
    var aKeys := GetPythonEngine.PyDict_Keys(aGlobals);
    try
      for var I := 0 to GetPythonEngine.PySequence_Length(aKeys) - 1 do
      begin
        var aKey := GetPythonEngi…

Replies: 7 comments · 4 replies

Comment options

Run:
del rb

don't like to be forced to release these objects by hands.

I don't know how to do it automatically

You must be logged in to vote
1 reply
@MKostitsyn
Comment options

rb = None also works fine, but I do want to do it automatically.

In my Delphi project I have list of produced PyObjects, but of course I don't know names of the variables that store them.
Now I think, GlobalVars/LocalVars dictionaries could help me somehow.

Comment options

AFAIR rb = None only zeros the pointer to the var. But value of var is kept.

You must be logged in to vote
0 replies
Comment options

The following code solves my issue. I hope it will be useful for someone else.
fPyDelphiObjects - is a TDictionary<string, PPyObject>, where I keep all the P4D objects, that were created during script execution.
So this code is kinda my own garbage collector.

I would also appreciate to receive any comments on this method, if it has mistakes or vulnerables.

procedure TDeepyPythonModule.ReleasePyObjects;
begin
  var aScript := TStringList.Create;
  try
    var aGlobals := GetPythonEngine.EvalString('globals()');
    var aKeys := GetPythonEngine.PyDict_Keys(aGlobals);
    try
      for var I := 0 to GetPythonEngine.PySequence_Length(aKeys) - 1 do
      begin
        var aKey := GetPythonEngine.PySequence_GetItem(aKeys, I);
        if aKey <> nil then
        try
          var aValue := GetPythonEngine.PyDict_GetItem(aGlobals, aKey);
          for var aPair in fPyDelphiObjects do
            if aPair.Value = aValue then
            begin
              var aKeyStr := GetPythonEngine.PyUnicodeAsString(aKey);
              aScript.Add('del ' + aKeyStr);
              Break;
            end;
        finally
          GetPythonEngine.Py_DECREF(aKey);
        end;
      end;
    finally
      GetPythonEngine.Py_xDECREF(aKeys);
      GetPythonEngine.Py_xDECREF(aGlobals);
    end;
    if aScript.Count > 0 then
      GetPythonEngine.ExecStrings(aScript);
  finally
    FreeAndNil(aScript);
  end;
end;
You must be logged in to vote
0 replies
Answer selected by MKostitsyn
Comment options

here is how to get globals w/o calculating 'globals()'

procedure TAppPython.InitModuleMain;
begin
  with FEngine do
    if ModuleMain=nil then
    begin
      ModuleMain:= PyImport_AddModule('__main__'); //same as PythonEngine.GetMainModule
      if ModuleMain=nil then
        raise EPythonError.Create('Python: cannot init __main__');
      if GlobalsMain=nil then
        GlobalsMain:= PyModule_GetDict(ModuleMain);
    end;
end;
You must be logged in to vote
1 reply
@MKostitsyn
Comment options

Why is it better? Will it save a lot of resources?

Comment options

also: you call GetPythonEngine 10 times. cache it to var.

You must be logged in to vote
0 replies
Comment options

also:
see https://stackoverflow.com/questions/21514631/how-to-delete-an-instantiated-object-python
If i got the idea: running 'del x' is the same as calling DLL API: which decreases ref count. Py_xDECREF .

You must be logged in to vote
1 reply
@MKostitsyn
Comment options

This was my first shot - just to iterate the fPyDelphiObjects dict and to decrease the reference counter, but it lead to the AV in the following scripts when using the same variable.
It seems, Python Engine did not know, that the object was already released.

Comment options

I have set of scripts that produce and use objects like this:

from pc1 import Pc as pc
rb = pc.ReceiptBuilder()
# work with receipt builder
rb = None

If I comment the last line, rb seems not to be released automatically. How can this be avoided? I would like not to have zombie-objects before I run next script. I also don't like to be forced to release these objects by hands.

You can change your code to the following so that you do not pollute the main module dictionary.

def dowork():
    from pc1 import Pc as pc
    rb = pc.ReceiptBuilder()
    # work with receipt builder

dowork()
You must be logged in to vote
1 reply
@MKostitsyn
Comment options

I'm just a beginner in Python.
Is this approach suitable for any possible scenario? For example with local functions/classes/threads, with importing libraries, and so on and so on?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Category
🙏
Q&A
Labels
None yet
3 participants
Morty Proxy This is a proxified and sanitized view of the page, visit original site.