Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Errors cannot be printed/formatted in 3.12+ because DynamicScope object is not iterable #565

Closed
frmdstryr opened this issue Jan 10, 2025 · 8 comments

Comments

@frmdstryr
Copy link
Contributor

It seems any error in an enaml expression/event callback with python 3.12+ causes the application to sigabort because errors cannot be printed (by pyqt or any other logging).

For example

import traceback
from atom.api import Atom, Str, Int, Instance, List
from enaml.widgets.api import Window, Container, Label, PushButton


def log_error(e):
    traceback.print_exception(e)

enamldef Main(Window):
    Container:
        Label:
            text = "Hello"
        PushButton:
            text = "Click me"
            clicked ::
                try:
                    print(missing_var)
                except Exception as e:
                    log_error(e)

Causes this:

Traceback (most recent call last):
  File "tests/error.enaml", line 16, in f
    try:
NameError: name 'missing_var' is not defined

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/jrm/projects/enaml/enaml/qt/qt_abstract_button.py", line 83, in on_clicked
    self.declaration.clicked(checked)
  File "/home/jrm/projects/enaml/enaml/core/declarative_meta.py", line 72, in declarative_change_handler
    engine.write(owner, change['name'], change)
  File "/home/jrm/projects/enaml/enaml/core/expression_engine.py", line 210, in write
    pair.writer(owner, name, change)
  File "/home/jrm/projects/enaml/enaml/core/standard_handlers.py", line 82, in __call__
    call_func(func, (), {}, scope)
  File "tests/error.enaml", line 19, in f
    log_error(e)
  File "tests/error.enaml", line 7, in log_error
    traceback.print_exception(e)
  File "/home/jrm/.local/share/mamba/envs/enaml/lib/python3.12/traceback.py", line 124, in print_exception
    te = TracebackException(type(value), value, tb, limit=limit, compact=True)
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/jrm/.local/share/mamba/envs/enaml/lib/python3.12/traceback.py", line 767, in __init__
    suggestion = _compute_suggestion_error(exc_value, exc_traceback, wrong_name)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/jrm/.local/share/mamba/envs/enaml/lib/python3.12/traceback.py", line 1091, in _compute_suggestion_error
    list(frame.f_locals)
TypeError: 'enaml.dynamicscope.DynamicScope' object is not iterable
Aborted (core dumped)

Without the try/except it occurs in pyqt.

Thread 1 "python" received signal SIGABRT, Aborted.
__pthread_kill_implementation (no_tid=0, signo=6, threadid=<optimized out>) at ./nptl/pthread_kill.c:44
warning: 44     ./nptl/pthread_kill.c: No such file or directory
(gdb) bt
#0  __pthread_kill_implementation (no_tid=0, signo=6, threadid=<optimized out>) at ./nptl/pthread_kill.c:44
#1  __pthread_kill_internal (signo=6, threadid=<optimized out>) at ./nptl/pthread_kill.c:78
#2  __GI___pthread_kill (threadid=<optimized out>, signo=signo@entry=6) at ./nptl/pthread_kill.c:89
#3  0x00007ffff7c4526e in __GI_raise (sig=sig@entry=6) at ../sysdeps/posix/raise.c:26
#4  0x00007ffff7c288ff in __GI_abort () at ./stdlib/abort.c:79
#5  0x00007ffff5cbd012 in ?? () from /home/jrm/.local/share/mamba/envs/enaml/lib/python3.12/site-packages/PyQt6/Qt6/lib/libQt6Core.so.6
#6  0x00007ffff5cfaa45 in ?? () from /home/jrm/.local/share/mamba/envs/enaml/lib/python3.12/site-packages/PyQt6/Qt6/lib/libQt6Core.so.6
#7  0x00007ffff5cbe945 in QMessageLogger::fatal(char const*, ...) const () from /home/jrm/.local/share/mamba/envs/enaml/lib/python3.12/site-packages/PyQt6/Qt6/lib/libQt6Core.so.6
#8  0x00007ffff66456de in pyqt6_err_print() () from /home/jrm/.local/share/mamba/envs/enaml/lib/python3.12/site-packages/PyQt6/QtCore.abi3.so
#9  0x00007ffff664e2cd in PyQtSlotProxy::unislot(void**) () from /home/jrm/.local/share/mamba/envs/enaml/lib/python3.12/site-packages/PyQt6/QtCore.abi3.so
#10 0x00007ffff664f307 in PyQtSlotProxy::qt_metacall(QMetaObject::Call, int, void**) () from /home/jrm/.local/share/mamba/envs/enaml/lib/python3.12/site-packages/PyQt6/QtCore.abi3.so
#11 0x00007ffff5dde36c in ?? () from /home/jrm/.local/share/mamba/envs/enaml/lib/python3.12/site-packages/PyQt6/Qt6/lib/libQt6Core.so.6
#12 0x00007ffff14b9576 in QAbstractButton::clicked(bool) () from /home/jrm/.local/share/mamba/envs/enaml/lib/python3.12/site-packages/PyQt6/Qt6/lib/libQt6Widgets.so.6

In enaml-web any error now shuts down the whole application..

@MatthieuDartiailh
Copy link
Member

Wow this is bad ! I don not get why it leads to a segfault but we need to do better. I will look into it ASAP.

@frmdstryr
Copy link
Contributor Author

It's a sigabort not segfault.

Would it make sense to just have DynamicScope iterate over it's f_locals (even though it's not the full scope)?

@frmdstryr
Copy link
Contributor Author

frmdstryr commented Jan 10, 2025

It fixes the sigabort but provides no hints frmdstryr@8cca5aa

@sccolbert
Copy link
Member

sccolbert commented Jan 10, 2025 via email

@frmdstryr
Copy link
Contributor Author

I'm not sure it's something with bytecode... it looks like the DynamicScope is assigned right to the frame's f_local variable?

  1. StandardWriteHandler calls call_func with the scope as the last arg.
  2. call_func assigns that to func_locals and passes it to PyEval_EvalCodeEx as the locals arg in funchelper.cpp#L91
  3. PyEval_EvalCodeEx passes locals to in _PyEval_Vector as the 3rd arg which passes it to _PyEvalFramePushAndInit
  4. _PyEvalFramePushAndInit passes locals to _PyFrame_Initialize which assigns it as frame->f_locals

Since it's a name error the traceback looks in the locals to try and suggest a mistyped name at https://github.com/python/cpython/blob/main/Lib/traceback.py#L1507.

@frmdstryr
Copy link
Contributor Author

IPython also can't be embedded in a event handler/d func for a similar reason as it tries to access .items() on the DynamicScope.

Traceback (most recent call last):
  File "tests/error.enaml", line 20, in f
    import IPython; IPython.embed()
                    ^^^^^^^^^^^^^^^
  File "/home/jrm/.local/share/mamba/envs/enaml/lib/python3.12/site-packages/IPython/terminal/embed.py", line 415, in embed
    shell(header=header, stack_depth=2, compile_flags=compile_flags,
  File "/home/jrm/.local/share/mamba/envs/enaml/lib/python3.12/site-packages/IPython/terminal/embed.py", line 251, in __call__
    self.mainloop(
  File "/home/jrm/.local/share/mamba/envs/enaml/lib/python3.12/site-packages/IPython/terminal/embed.py", line 330, in mainloop
    reentrant_local_ns = {k: v for (k, v) in local_ns.items() if k not in self.user_ns_hidden.keys()}
                                             ^^^^^^^^^^^^^^
AttributeError: 'enaml.dynamicscope.DynamicScope' object has no attribute 'items'

@sccolbert
Copy link
Member

Interesting. Good finds. I wonder if this is a Py 2 vs 3 change. IIRC in Py 2, the f_locals only conformed to the mapping protocal, hence this check:

https://github.com/nucleic/enaml/blob/main/enaml/src/funchelper.cpp#L53

So if f_locals in Py3 needs a more complex interface, then we need to implement that for DynamicScope.

@frmdstryr
Copy link
Contributor Author

Fixed by #571

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants