pyscripter / python4delphi

Free components that wrap up Python into Delphi and Lazarus (FPC)
MIT License
895 stars 307 forks source link

"demo33 -- three interpreter" causes the thread to hang. #450

Closed theoak2 closed 7 months ago

theoak2 commented 7 months ago

The test code consists of a single line: "import ddddocr." Executing this line using the "one interpreter" approach in demo33 poses no issues. However, when executing the same line using the "three interpreter" approach in demo33, the thread hangs at the line PyEval_EvalCode within TPythonEngine.Run_CommandAsObjectWithDict.

Test Method: Modify the ExecuteWithPython method in demo33 as follows:

procedure TSortThread.ExecuteWithPython;
begin
  running := true;
  try
    with GetPythonEngine do
      ExecString('import ddddocr');
  finally
    running := false;
  end;
end;
pyscripter commented 7 months ago

The demo works as expected. It is not designed to execute any python code unrelated to sorting. Study this demo to learn about running python code in threads. See also https://github.com/pyscripter/python4delphi/wiki/PythonThreads.

theoak2 commented 7 months ago

Running this code in demo33 is just to describe the issue. In fact, as long as ThreadExecMode is set to emNewInterpreter or emNewInterpreterOwnGIL, this issue will occur when running this line of code. Is there any way to avoid this problem?

pyscripter commented 7 months ago

In the demo emNewState was treated differently than the other modes. The contents of the memo were executed in the main thread. So you did not get the hang application.

I have updated the demo so that:

theoak2 commented 7 months ago

If ThreadExecMode is set to emNewInterpreter or emNewInterpreterOwnGIL, I have consistently been unsuccessful in running that line of code.

Indeed, this line of code can be executed in emNewState mode, but the PythonEngine.ExecString retains previous code functionality in memory after execution. Because I also need to run other different code within this thread, so, I have to clear various caches after running it. When I finalize and then initialize again, running other simple code works fine, but as soon as I run the "import ddddocr" line, an error occurs.

Could you please tell me how to repeatedly "initialize PythonEngine, run Python, and release PythonEngine" within a thread? Thank you very much!

pyscripter commented 7 months ago

I have no idea what you are trying to do and why, but this may be helpful. See https://github.com/pyscripter/python4delphi/wiki/PythonThreads. Note that you will not get very far by trial and error. This is an advanced use of python and you need to have a good understanding of GIL, module initiallization and related issues.

emNewState maintains previous state (imports, global variables etc.), whilst the other two modes don't.

theoak2 commented 7 months ago

I have carefully read this article but still couldn't solve the problem.

I think if I could clear the state preserved by emNewState, it might resolve my issue.

What I'm trying to do is: I have an application similar to a web scraper. When scraping different websites, I need to set different post-processing actions for them. Because Python makes it more convenient to implement these actions, I want to embed Python into the application. Since the websites are different, the code to be executed varies, and I need to avoid the impact of the code executed in the previous session on the next action. Therefore, I need emNewInterpreter and emNewInterpreterOwnGIL to not preserve the previous state. However, these two modes cannot execute some code (for example "import ddddocr"), so they cannot be used.

pyscripter commented 7 months ago

This is not a P4D issue but a python issue. Not all modules can be safely imported in sub-interpreters. In particular the modules that do not implement https://peps.python.org/pep-0489/ may cause trouble.