sagemath / sage

Main repository of SageMath
https://www.sagemath.org
Other
1.45k stars 482 forks source link

No tracebacks when loading a .sage file #22167

Open jdemeyer opened 7 years ago

jdemeyer commented 7 years ago

Create a file toto.sage:

raise RuntimeError("toto")

Now in Sage:

sage: load("toto.sage")
---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
<ipython-input-1-1b599e37ac0a> in <module>()
----> 1 load("toto.sage")

/usr/local/src/sage-git/src/sage/structure/sage_object.pyx in sage.structure.sage_object.load (build/cythonized/sage/structure/sage_object.c:12529)()
    999 
   1000     if sage.repl.load.is_loadable_filename(filename):
-> 1001         sage.repl.load.load(filename, globals())
   1002         return
   1003 

/usr/local/src/sage-git/local/lib/python2.7/site-packages/sage/repl/load.pyc in load(filename, globals, attach)
    245             if attach:
    246                 add_attached_file(fpath)
--> 247             exec(preparse_file(open(fpath).read()) + "\n", globals)
    248     elif ext == '.spyx' or ext == '.pyx':
    249         if attach:

<string> in <module>()

RuntimeError: toto

This should show a traceback involving the toto.sage file (or at least the pre-processed version of it, see also #71). This could be implemented using some magic involving linecache.

CC: @zimmermann6

Component: user interface

Issue created by migration from https://trac.sagemath.org/ticket/22167

jdemeyer commented 7 years ago

Description changed:

--- 
+++ 
@@ -32,4 +32,4 @@
 RuntimeError: toto

-This should show a traceback involving the toto.sage file (or at least the pre-processed version of it, see also #71). +This should show a traceback involving the toto.sage file (or at least the pre-processed version of it, see also #71). This could be implemented using some magic involving linecache.

user202729 commented 1 year ago

With linecache magic it could be implemented like this:

(written as a cherry-patch over SageMath's code, to be executed outside)

    import linecache
    from pathlib import Path
    _old_load=load.load
    _old_load = getattr(_old_load, "__before_patch__", _old_load)
    from sage.repl.preparse import preparse_file
    def new_load(filename, globals, attach=False):
        if isinstance(filename, str) and not attach:
            p=Path(filename)
            if p.is_file() and p.suffix==".sage":
                code=preparse_file(p.read_text()) + "\n"
                # https://stackoverflow.com/questions/47183305/file-string-traceback-with-line-preview
                sourcename: str="<SageMath preparsed>"
                i=0
                while sourcename in linecache.cache:
                    sourcename="<SageMath preparsed " + str(i) + ">"
                    i+=1

                lines=code.splitlines(keepends=True)
                linecache.cache[sourcename] = len(code), None, lines, sourcename

                compiled_code=compile(code, sourcename, "exec")
                exec(compiled_code, globals)
                return

        _old_load(filename=filename, globals=globals, attach=attach)

    new_load.__before_patch__=_old_load
    load.load=new_load

although the disadvantage is that it will not correctly handle errors printed by the C code. In practice it works okay however.

https://stackoverflow.com/questions/47183305/file-string-traceback-with-line-preview

Another disadvantage is that it must cause a "memory leak" by not deleting the linecache even after the code finishes executing, as it's possible for the code to define some function to be called later.