zMotoR 0 Posted February 25, 2021 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() Share this post Link to post
David Heffernan 2353 Posted February 25, 2021 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
zMotoR 0 Posted February 25, 2021 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
David Heffernan 2353 Posted February 25, 2021 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
zMotoR 0 Posted February 25, 2021 "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
David Heffernan 2353 Posted February 25, 2021 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
Remy Lebeau 1436 Posted February 25, 2021 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
zMotoR 0 Posted February 26, 2021 There are no exceptions after exit() at all. Nothing to catch. 😞 Share this post Link to post
fjames 0 Posted February 27, 2021 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
zMotoR 0 Posted February 27, 2021 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
pyscripter 694 Posted February 27, 2021 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
zMotoR 0 Posted February 28, 2021 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
David Heffernan 2353 Posted February 28, 2021 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
pyscripter 694 Posted February 28, 2021 (edited) 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 February 28, 2021 by pyscripter Share this post Link to post
pyscripter 694 Posted February 28, 2021 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
pyscripter 694 Posted February 28, 2021 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
David Heffernan 2353 Posted February 28, 2021 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. 1 Share this post Link to post
Cristian Peța 107 Posted February 28, 2021 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
pyscripter 694 Posted February 28, 2021 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
pyscripter 694 Posted March 1, 2021 Fixed in version control. See https://github.com/pyscripter/python4delphi/issues/292 Now SystemExit results in a EPySystemExit exception which can be handled in Delphi code. Process termination is prevented. 2 Share this post Link to post