-
-
Notifications
You must be signed in to change notification settings - Fork 324
How do I free objects after the script execution? #455
-
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.
Beta Was this translation helpful? Give feedback.
All reactions
If you want to clean up your created global variables, the best way is to use the following ExecString/EvalString overloads:
procedure ExecString(const command: AnsiString; locals, globals: PPyObject; const FileName: string = '<string>'); overload; function EvalString(const command: AnsiString; locals, globals: PPyObject; const FileName: string = '<string>'): PPyObject; overload;
and provide a new locals dictionary (PyDict_New), which you destroy (Py_DECREF) after the calling the above functions.
Note that if globals is nil, it becomes the same as locals.
Replies: 8 comments 4 replies
-
Run:
del rb
don't like to be forced to release these objects by hands.
I don't know how to do it automatically
Beta Was this translation helpful? Give feedback.
All reactions
-
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.
Beta Was this translation helpful? Give feedback.
All reactions
-
AFAIR rb = None only zeros the pointer to the var. But value of var is kept.
Beta Was this translation helpful? Give feedback.
All reactions
-
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;
Beta Was this translation helpful? Give feedback.
All reactions
-
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;
Beta Was this translation helpful? Give feedback.
All reactions
-
Why is it better? Will it save a lot of resources?
Beta Was this translation helpful? Give feedback.
All reactions
-
also: you call GetPythonEngine 10 times. cache it to var.
Beta Was this translation helpful? Give feedback.
All reactions
-
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 .
Beta Was this translation helpful? Give feedback.
All reactions
-
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.
Beta Was this translation helpful? Give feedback.
All reactions
-
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 = NoneIf 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()
Beta Was this translation helpful? Give feedback.
All reactions
-
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?
Beta Was this translation helpful? Give feedback.
All reactions
-
If you want to clean up your created global variables, the best way is to use the following ExecString/EvalString overloads:
procedure ExecString(const command: AnsiString; locals, globals: PPyObject; const FileName: string = '<string>'); overload; function EvalString(const command: AnsiString; locals, globals: PPyObject; const FileName: string = '<string>'): PPyObject; overload;
and provide a new locals dictionary (PyDict_New), which you destroy (Py_DECREF) after the calling the above functions.
Note that if globals is nil, it becomes the same as locals.
Beta Was this translation helpful? Give feedback.