3

The goal is to get the os dependency resolved in the code below and the ls command executed.

I want to know how to do what I have put in comment below with the double stars.

I know already that I can get the line number trough the traceback

Namely, I want to know if it is possible to resume the execution of the program at a given line number in a given context.

import sys

def new_sys_excepthook(type, value, traceback):
    if type == NameError:
        pass
        # Resolution of the exception finding the module dependency and doing the import
        # ** Returning to the line that created the exception with the right
        # ** context and continue the execution with the module 
        # ** dependency resolved 
    # call original excepthook if we can't solve the issue
    sys.__excepthook__(type, value, traceback) 

sys.excepthook = new_sys_excepthook

system("ls")
lc2817
  • 3,722
  • 16
  • 40
  • 1
    I understand that you have to use custom excepthook instead of simple try except block, is that right? – Pawel Miech Sep 19 '13 at 06:22
  • Yes because the final goal is to make a library (that will override the global exception hook) doing the `__import__` automatically when the exception occurs. – lc2817 Sep 19 '13 at 06:25
  • 2
    What is the *real* intention here? A kind of DSL that people not knowing the stdlib can use? A way better course of action would be to exec that code with a magic global dict, that defines `__getitem__` accordingly. Still a hack, but way less ugly and intrusive. – jhermann Sep 21 '13 at 08:23
  • Could you elaborate the magic global dict? The intention is to do module import on the fly when there is one choice (this is not a serious project, just kidding around to see what is doable). – lc2817 Sep 21 '13 at 08:33
  • 2
    The basic answer is: you can't. The original context is no longer there, the exception has been caught at the top level and all that will happen now is that the interpreter **will** exit. – Martijn Pieters Sep 21 '13 at 10:19
  • What are you trying to do, really? – Martijn Pieters Sep 21 '13 at 10:19
  • A library that you can import that will resolve the import on the fly instead of having to specify them, the code above explains what I want to achieve. – lc2817 Sep 21 '13 at 10:38

1 Answers1

5

The only way to do arbitrary jumps of this sort is in CPython, and is an implementation detail by the name of sys.settrace.

Here's a method, taken from the humorous goto April fools module, that lets me jump to (seemingly) arbitrary lines:

import sys
import inspect

_line_number = None
_frame = None
def jump_to(line_number, frame):
    global _line_number, _frame
    print("Set jump to line", line_number, "in", inspect.getfile(frame))

    _frame = frame
    _line_number = line_number


def _trace(frame, event, arg):
    global _line_number, _frame

    try:
        if _line_number is not None:
            if inspect.getfile(_frame) == inspect.getfile(frame):
                print("Jumping to line", _line_number, "in", inspect.getfile(frame))
                frame.f_lineno = _line_number
                _line_number = None

    except ValueError as e:
        print(e)

    return _trace

def install():
    sys.settrace(_trace)
    frame = sys._getframe().f_back
    while frame:
        frame.f_trace = _trace
        frame = frame.f_back

If I run it like this:

import traceh
traceh.install()

import inspect

traceh.jump_to(10, inspect.currentframe())
print(1)
print(2)
print(3)
print(4)
print(5)
print(6)

I get the exciting output:

Set jump to line 10 in tr.py
Jumping to line 10 in tr.py
4
5
6

Now we can embed that with sys.excepthook, surely?

...
def new_sys_excepthook(type, value, traceback):
    if type == NameError:
        jump_to(traceback.tb_lineno, traceback.tb_frame)
        traceback.tb_frame
        return

    sys.__excepthook__(type, value, traceback)


def install():
    sys.excepthook = new_sys_excepthook

    sys.settrace(_trace)
    ...

And to use it:

import traceh
traceh.install()

raise NameError
print(5)
print(6)

And the output...

Set jump to line 4 in tr.py

The problem is obvious: once sys.excepthook is called, the outer scope is gone so at no point does _trace get a chance to run in the original file!

What if we assume that has a solution, and go back to using jump_to for a moment?

import traceh
traceh.install()

import inspect

try:
    raise NameError
    print(1)
    print(2)
    print(3)
    print(4)
    print(5)
    print(6)

except:
    traceh.jump_to(10, inspect.currentframe())

This is immune to the problem we saw last time, because we're manually calling jump_to inside the file. Let's see:

Set jump to line 10 in tr.py
Jumping to line 10 in tr.py
can't jump into the middle of a block

Well damnit.

Idea credit goes heavily to the goto module by Richie Hindle.

Veedrac
  • 58,273
  • 15
  • 112
  • 169