Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to continue a frame execution from last attempted instruction after handling an exception?

Tags:

python

I would like to handle a NameError exception by injecting the desired missing variable into the frame and then continue the execution from last attempted instruction.

The following pseudo-code should illustrate my needs.

def function():
    return missing_var

try:
    print function()

except NameError:

    frame = inspect.trace()[-1][0]

    # inject missing variable
    frame.f_globals["missing_var"] = ...

    # continue frame execution from last attempted instruction
    exec frame.f_code from frame.f_lasti

Read the whole unittest on repl.it

Notes

  • As pointed out by ivan_pozdeev in his answer, this is known as resumption.
  • After more research, I found Veedrac's answer to the question Resuming program at line number in the context before an exception using a custom sys.excepthook posted by lc2817 very interesting. It relies on Richie Hindle's work.

Background

The code runs in a slave process, which is controlled by a parent. Tasks (functions really) are written in the parent and latter passed to the slave using dill. I expect some tasks (running in the slave process) to try to access variables from outer scopes in the parent and I'd like the slave to request those variables to the parent on the fly.

p.s.: I don't expect this magic to run in a production environment.

like image 584
Jules Baratoux Avatar asked Dec 22 '15 22:12

Jules Baratoux


People also ask

How do you continue a program execution after an exception in Python?

Perhaps after the first for-loop, add the try/except . Then if an error is raised, it will continue with the next file. This is a perfect example of why you should use a with statement here to open files. When you open the file using open() , but an error is catched, the file will remain open forever.

What is exception Assembly?

The idea of exception handling (often called "Structured Exception Handling") is that your application instals one or more callback routines called "exception handlers" at run-time and then, if an exception occurs, the system will call the routine to let the application deal with the exception.


3 Answers

On the contrary to what various commenters are saying, "resume-on-error" exception handling is possible in Python. The library fuckit.py implements said strategy. It steamrollers errors by rewriting the source code of your module at import time, inserting try...except blocks around every statement and swallowing all exceptions. So perhaps you could try a similar sort of tactic?

It goes without saying: that library is intended as a joke. Don't ever use it in production code.


You mentioned that your use case is to trap references to missing names. Have you thought about using metaprogramming to run your code in the context of a "smart" namespace such as a defaultdict? (This is perhaps only marginally less of a bad idea than fuckit.py.)

from collections import defaultdict

class NoMissingNamesMeta(type):
    @classmethod
    def __prepare__(meta, name, bases):
        return defaultdict(lambda: "foo")

class MyClass(metaclass=NoMissingNamesMeta):
    x = y + "bar"  # y doesn't exist

>>> MyClass.x
'foobar'

NoMissingNamesMeta is a metaclass - a language construct for customising the behaviour of the class statement. Here we're using the __prepare__ method to customise the dictionary which will be used as the class's namespace during creation of the class. Thus, because we're using a defaultdict instead of a regular dictionary, a class whose metaclass is NoMissingNamesMeta will never get a NameError. Any names referred to during the creation of the class will be auto-initialised to "foo".

This approach is similar to @AndréFratelli's idea of manually requesting the lazily-initialised data from a Scope object. In production I'd do that, not this. The metaclass version requires less typing to write the client code, but at the expense of a lot more magic. (Imagine yourself debugging this code in two years, trying to understand why non-existent variables are dynamically being brought into scope!)

like image 198
Benjamin Hodgson Avatar answered Oct 22 '22 20:10

Benjamin Hodgson


The "resumption" exception handling technique has proven to be problematic, that's why it's missing from C++ and later languages.

Your best bet is to use a while loop to not resume where the exception was thrown but rather repeat from a predetermined place:

while True:
    try:
        do_something()
    except NameError as e:
        handle_error()
    else:
        break
like image 3
ivan_pozdeev Avatar answered Oct 22 '22 19:10

ivan_pozdeev


You really can't unwind the stack after an exception is thrown, so you'd have to deal with the issue before hand. If your requirement is to generate these variables on the fly (which wouldn't be recommended, but you seem to understand that), then you'd have to actually request them. You can implement a mechanism for that (such as having a global custom Scope class instance and overriding __getitem__, or using something like the __dir__ function), but not as you are asking for it.

like image 3
André Fratelli Avatar answered Oct 22 '22 19:10

André Fratelli