Jump to content
zMotoR

exit terminates delphi app

Recommended Posts

Hello. I faced with a problem.. exit command in a python script terminates a delphi app. I use Demo1 app for testing. How to prevent terminating the app if I use exit command? Or how to get around the problem? I use this simple script to test:

 

exit()

 

python1.png

Share this post


Link to post

This looks like it should be in the python4delphi area of the site. 

 

If you don't want exit to terminate the process then don't call exit(). It's really that simple. 

 

For sure you could catch that exception which IIRC is SystemExit. But that doesn't make sense to me. If you don't want to exit, don't exit. 

Share this post


Link to post

Is it possible to catch SystemExit inside the delphi app?

 

I'd like to stop script running via exit() instead of terminating the app. How to do it?

Share this post


Link to post
31 minutes ago, zMotoR said:

Is it possible to catch SystemExit inside the delphi app?

I wouldn't have thought so. You asked for the process to be terminated.

 

I'm pretty sure that you have selected the wrong solution to your problem, and are trying to find a way to make it work.  You should step back and find the right solution.

Share this post


Link to post

"Not using exit()" is not what i'm looking for. I need a solution to prevent terminating the app from python script. Do you know how to do it?

Share this post


Link to post

We are going round in circles. I wouldn't call exit if I wanted the process not to be terminated. I can't for the life of me imagine why you want to call exit otherwise.

 

That said, in my own wrapper of embedded Python, if I call PyRun_StringFlags passing exit() then it doesn't terminate the process, and the call returns an error with a traceback.  So maybe python4delphi is doing something else.  Whilst I'm familiar with calling embedded Python from Delphi, I use my own wrapper.  You probably need specific python4delphi knowledge, but you asked the question in the VCL sub-forum by mistake. Perhaps you need to ask a mod to migrate it?

Share this post


Link to post
11 hours ago, zMotoR said:

Is it possible to catch SystemExit inside the delphi app?

Are you SURE your entire process just terminates immediately when exit() is called, and you are not simply failing to catch an exception that causes your main thread to end?  Have you wrapped the Python4Delphi code in a try/except block?  I am not familiar with Python4Delphi, but after a quick look through its source code, it appears like it might catch a Python SystemExit exception and raise a Delphi-style EPySystemExit exception into user code.  Not sure about that, though.  Have you tried catching EPySystemExit?

Share this post


Link to post

There are no exceptions after exit() at all. Nothing to catch. 😞

Share this post


Link to post
7 hours ago, zMotoR said:

There are no exceptions after exit() at all. Nothing to catch. 😞

This is not true. "exit()" raises a Python SystemExit exception which you can certainly catch.

 

If your change your Demo01 code to be:
 

try:
  exit()
except SystemExit as e:
  print("Python tried to Exit!")

The top TMemo will be populated with "Python tried to Exit!" and the application will stay open.

So you could easily modify an app to do this in the background.

Oddly enough, looking at TPythonEngine.RaiseError, the code is attempting to catch SystemExits and raise a Delphi exception. 

Share this post


Link to post

When I was talking about errors on exit() I mean a Delphi side, not a Python side. On a Delphi side I don't know that the app is terminating. No main form events (OnClose, OnDestroy, etc), no exceptions.

Share this post


Link to post

exit() raises the SystemExit exception, which unlike other exceptions, results in process termination before control gets back to Delphi.  To prevent process termination you can do one of the following:

 

  • wrap the python code in a try: except SystemExit: as suggested by @fjames above
  • "overwrite" the exit method by for example running the following code after the python engine is initialized:
def _exit():
    print("exit prevented")

__builtins__.exit = _exit
import sys
sys.exit = _exit

del(_exit)
  • overwrite the SystemExit exception!

Share this post


Link to post

All these decisions are on the Python side. 😞 I need a decision on a Delphi side, without changing the Python script.

Share this post


Link to post
38 minutes ago, zMotoR said:

All these decisions are on the Python side. 😞 I need a decision on a Delphi side, without changing the Python script.

If you read @pyscripter's comment you will realise that what you need is impossible. Usually in the face of impossibility it's time to look for other approaches.

 

I was able to catch SystemExit when I ran a single line of code using PyRun_StringFlags but I guess you are executing a module and I suppose that is treated differently. 

 

Anyway, it's time to recalibrate expectations. 

Share this post


Link to post
4 hours ago, zMotoR said:

All these decisions are on the Python side. 😞

All of these suggestions can be implemented without modifying your python code.

 

For example you can just add the suggested code to the PythonEngine.InitScript.

Edited by pyscripter

Share this post


Link to post
On 2/25/2021 at 1:56 PM, David Heffernan said:

 in my own wrapper of embedded Python

Any reason for not using P4D?

Share this post


Link to post
3 hours ago, David Heffernan said:

I was able to catch SystemExit when I ran a single line of code using PyRun_StringFlags

Which flag do you pass to PyRun_StringFlags?

Share this post


Link to post
37 minutes ago, pyscripter said:

Which flag do you pass to PyRun_StringFlags?

I called it like this:

 

procedure TPythonEngine.Execute(const Code: PAnsiChar; const globals, locals: IPyDictionary);
var
  retval: PPyObjectRef;
begin
  retval := PyRun_StringFlags(Code, Py_file_input, globals.Ref, locals.Ref, nil);
  CheckError(Assigned(retval));
  DecRef(retval);
end;

I passed exit() in via the Code argument, and empty dicts in globals and locals. retval comes back nil and CheckError is my function to extract tracebacks.

42 minutes ago, pyscripter said:

Any reason for not using P4D?

I know we looked at way back when we first embedded Python. I honestly don't recall the details. I think some part of the decision will be my own fastidiousness to have total control over everything, and as you know P4D has a huge range of functionality. We only scratch at the surface of what P4D can do.

 

Our usage is a finite element structural engineering code which allows users to customise some parts of the calculation and post-processing by calling Python code that they provide. So we just need to create instances, populate attributes, and call the user function passing in those instances. Then we need to read out the attributes from those instances after the user script has run. It's such a tiny part of what P4D offers, that we just did it ourself.

 

The way our wrapper exposes Python is much closer to the metal than P4D I believe. We use interfaces to wrap Python objects, and because they are also reference counter, that fits really nicely with the Python ref count. When our implementing wrapper objects are destroyed (because the Delphi ref count goes to zero), we just call Py_DecRef on the underlying Python object. I seem to recall that in P4D client code you sometimes have to deal with the Python ref code, but we've been able to hide that from our client code.

 

Anyway, embedded Python is a truly remarkable achievement, and P4D is clearly a fantastic library. Our software would be far poorer without embedded Python.

  • Like 1

Share this post


Link to post
15 hours ago, pyscripter said:
  • "overwrite" the exit method by for example running the following code after the python engine is initialized:

def _exit():
    print("exit prevented")

__builtins__.exit = _exit
import sys
sys.exit = _exit

del(_exit)

@zMotoR if you are giving to the user the ability to modify scripts and you are afraid that someone will write an exit(), this looks like a nice method that can be easily applied automatically in the background without the user knowledge.

Share this post


Link to post
3 hours ago, David Heffernan said:

We use interfaces to wrap Python objects, and because they are also reference counter, that fits really nicely with the Python ref count.

P4D does something similar with custom variants and the Varpyth unit.

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

×