enable icon indicating copy to clipboard operation
enable copied to clipboard

libc++abi exception when running demo enable/enable/examples/demo/savage/buttons_on_canvas when running python3.11

Open homosapien-lcy opened this issue 1 year ago • 15 comments

Description:

When running demo enable/enable/examples/demo/savage/buttons_on_canvas, a exception will happen in libc++:

2023-04-06 11:25:18.884 Python[3819:77967] ApplePersistenceIgnoreState: Existing state will not be touched. New state will be written to /var/folders/2z/kylzj9s92y71cxscmljmpqrh0000gt/T/org.python.python.savedState
libc++abi: terminating with uncaught exception of type kiva::$_0
zsh: abort      python3.11 enable/examples/demo/savage/buttons_on_canvas.py

Replication step: python enable/examples/demo/savage/buttons_on_canvas.py

Environment: MacOS, python3.11 or python3.8 (with or without EDM)

Packages Installed:

Package            Version     Editable project location
------------------ ----------- --------------------------------------
chaco              5.1.0       /Users/cyliu/Documents/3.11_test/chaco
configobj          5.0.8
enable             5.4.0.dev32
fonttools          4.39.2
joblib             1.2.0
numpy              1.24.2
pandas             1.5.3
Pillow             9.4.0
pip                22.3.1
PyAudio            0.2.13
pyface             7.4.4
pygarrayimage      1.0
pyglet             2.0.5
Pygments           2.14.0
pyparsing          3.0.9
PySide6            6.4.3
PySide6-Addons     6.4.3
PySide6-Essentials 6.4.3
python-dateutil    2.8.2
pytz               2023.2
reportlab          3.6.12
scikit-learn       1.2.2
scipy              1.10.1
setuptools         65.6.3
shiboken6          6.4.3
six                1.16.0
threadpoolctl      3.1.0
traits             6.4.1
traitsui           8.0.0.dev0

homosapien-lcy avatar Apr 06 '23 02:04 homosapien-lcy

The error seems to happen in demo.configure_traits() (enable/examples/demo/savage/buttons_on_canvas.py, Line 181). Evidence: When I add prints above and below it, only the first print (1) will print out:

demo = ButtonCanvasView()

if __name__ == "__main__":
    print("1")
    demo.configure_traits()
    print("2")

homosapien-lcy avatar Apr 06 '23 04:04 homosapien-lcy

After further locating using the same print method, the error is found inside the following part of the traits/traits/has_traits.py (line 2163-2172). This is a class from traitsui:


rc = toolkit().view_application(
                context,
                self.trait_view(view),
                kind,
                handler,
                id,
                scrollable,
                args,
            )

homosapien-lcy avatar Apr 06 '23 06:04 homosapien-lcy

We can exclude the possibility that the error comes from toolkit() construction function (traitsui/traitsui/toolkit.py line 80), since the print can successfully pass this function. The inspect tool points to the overrided view_application function in traitsui/qt4/toolkit.py using the following code (in line 2164 of traits/traits/has_traits.py)

print(inspect.getsourcefile(toolkit().view_application))

homosapien-lcy avatar Apr 06 '23 06:04 homosapien-lcy

Further print tests leads to

traitsui/qt4/view_application.py (line 59-110)

def view_application(context, view, kind, handler, id, scrollable, args):
    """Creates a stand-alone PyQt application to display a specified traits UI
        View.

    Parameters
    ----------
    context : object or dictionary
        A single object or a dictionary of string/object pairs, whose trait
        attributes are to be edited. If not specified, the current object is
        used.
    view : view object
        A View object that defines a user interface for editing trait attribute
        values.
    kind : string
        The type of user interface window to create. See the
        **traitsui.view.kind_trait** trait for values and
        their meanings. If *kind* is unspecified or None, the **kind**
        attribute of the View object is used.
    handler : Handler object
        A handler object used for event handling in the dialog box. If
        None, the default handler for Traits UI is used.
    scrollable : Boolean
        Indicates whether the dialog box should be scrollable. When set to
        True, scroll bars appear on the dialog box if it is not large enough
        to display all of the items in the view at one time.


    """
    if (kind == "panel") or ((kind is None) and (view.kind == "panel")):
        kind = "modal"

    app = QtGui.QApplication.instance()
    if app is None or not is_event_loop_running_qt4(app):
        return ViewApplication(
            context, view, kind, handler, id, scrollable, args
        ).ui.result

    ui = view.ui(
        context,
        kind=kind,
        handler=handler,
        id=id,
        scrollable=scrollable,
        args=args,
    )

    # If the UI has not been closed yet, we need to keep a reference to
    # it until it does close.
    if not ui.destroyed:
        KEEP_ALIVE_UIS.add(ui)
        ui.on_trait_change(on_ui_destroyed, "destroyed")
    return ui.result

homosapien-lcy avatar Apr 06 '23 07:04 homosapien-lcy

Is there no stack trace? Most of the code you're referencing here is nowhere near kiva.

jwiggins avatar Apr 06 '23 07:04 jwiggins

@homosapien-lcy It may be worth trying to run this under faulthandler to see if that gives useful output: https://docs.python.org/3/library/faulthandler.html.

mdickinson avatar Apr 06 '23 07:04 mdickinson

As John noted the code you have linked here is just generic GUI wrapper code that starts and runs a TraitsUI application and so is not helpful in diagnosing the issue - or likely at fault (by the way, github allows you to highlight and copy permanent links to other code stored in github, which is a nicer way of linking in issues). As Mark suggests, run with faulthandler to get a better idea of the issue.

corranwebster avatar Apr 06 '23 08:04 corranwebster

Thanks for the comments! Looking into faulthandler now

homosapien-lcy avatar Apr 06 '23 08:04 homosapien-lcy

Looking into faulthandler now

For the quick version: just use python -X faulthandler enable/examples/demo/savage/buttons_on_canvas.py instead of python enable/examples/demo/savage/buttons_on_canvas.py

mdickinson avatar Apr 06 '23 08:04 mdickinson

Thanks! I got much more information from the error message. It seems the problem comes from kiva/agg/agg.py", line 1183 in clip_to_rect:

Traceback (most recent call last):
  File "/Users/cyliu/Documents/3.11_test/enable/enable/examples/demo/savage/buttons_on_canvas.py", line 22, in <module>
    from enable.api import BaseTool, Component, ComponentEditor, Container
ModuleNotFoundError: No module named 'enable'
(py311) (base) cyliu@aus552cyliu enable % python3.11 -X faulthandler enable/examples/demo/savage/buttons_on_canvas.py
2023-04-06 17:31:16.778 Python[11186:238536] ApplePersistenceIgnoreState: Existing state will not be touched. New state will be written to /var/folders/2z/kylzj9s92y71cxscmljmpqrh0000gt/T/org.python.python.savedState
libc++abi: terminating with uncaught exception of type kiva::$_0
Fatal Python error: Aborted

Current thread 0x0000000119235600 (most recent call first):
  File "/Users/cyliu/.venvs/py311/lib/python3.11/site-packages/kiva/agg/agg.py", line 1183 in clip_to_rect
  File "/Users/cyliu/.venvs/py311/lib/python3.11/site-packages/enable/savage/svg/backends/kiva/renderer.py", line 218 in set_on_gc
  File "/Users/cyliu/.venvs/py311/lib/python3.11/site-packages/enable/savage/svg/backends/kiva/renderer.py", line 534 in gradientPath
  File "/Users/cyliu/.venvs/py311/lib/python3.11/site-packages/enable/savage/svg/document.py", line 1119 in render
  File "/Users/cyliu/Documents/3.11_test/enable/enable/examples/demo/savage/buttons_on_canvas.py", line 79 in _draw_svg_document
  File "/Users/cyliu/Documents/3.11_test/enable/enable/examples/demo/savage/buttons_on_canvas.py", line 59 in draw
  File "/Users/cyliu/Documents/3.11_test/enable/enable/examples/demo/savage/buttons_on_canvas.py", line 99 in draw
  File "/Users/cyliu/.venvs/py311/lib/python3.11/site-packages/enable/abstract_window.py", line 536 in _paint
  File "/Users/cyliu/.venvs/py311/lib/python3.11/site-packages/enable/qt4/base_window.py", line 90 in paintEvent
  File "/Users/cyliu/.venvs/py311/lib/python3.11/site-packages/enable/qt4/base_window.py", line 255 in paintEvent
  File "/Users/cyliu/.venvs/py311/lib/python3.11/site-packages/pyface/util/guisupport.py", line 155 in start_event_loop_qt4
  File "/Users/cyliu/.venvs/py311/lib/python3.11/site-packages/traitsui/qt4/view_application.py", line 138 in __init__
  File "/Users/cyliu/.venvs/py311/lib/python3.11/site-packages/traitsui/qt4/view_application.py", line 92 in view_application
  File "/Users/cyliu/.venvs/py311/lib/python3.11/site-packages/traitsui/qt4/toolkit.py", line 237 in view_application
  File "/Users/cyliu/.venvs/py311/lib/python3.11/site-packages/traits/has_traits.py", line 2164 in configure_traits
  File "/Users/cyliu/Documents/3.11_test/enable/enable/examples/demo/savage/buttons_on_canvas.py", line 181 in <module>

Extension modules: traits.ctraits, numpy.core._multiarray_umath, numpy.core._multiarray_tests, numpy.linalg._umath_linalg, numpy.fft._pocketfft_internal, numpy.random._common, numpy.random.bit_generator, numpy.random._bounded_integers, numpy.random._mt19937, numpy.random.mtrand, numpy.random._philox, numpy.random._pcg64, numpy.random._sfc64, numpy.random._generator, kiva._cython_speedups, kiva._marker_renderer, xxsubtype, shiboken6.Shiboken, PySide6.QtCore, PySide6.QtGui, PySide6.QtWidgets, PySide6.QtPrintSupport, PySide6.QtNetwork, PySide6.QtWebChannel, PySide6.QtWebEngineCore, PySide6.QtWebEngineWidgets, PySide6.QtSvg, kiva.agg._agg, kiva.agg._plat_support, PySide6.QtOpenGL, PySide6.QtOpenGLWidgets (total: 31)
zsh: abort      python3.11 -X faulthandler enable/examples/demo/savage/buttons_on_canvas.py

homosapien-lcy avatar Apr 06 '23 08:04 homosapien-lcy

Ahh... that's the level of detail we needed. Here's the relevant C++ code: https://github.com/enthought/enable/blob/ccb76800a11f1d0d2f6618bbf6a6498839ed62cb/kiva/agg/src/kiva_graphics_context.h#L862-L917

Yes, there is an else clause where an exception is thrown, but I don't think that's the exception here. In order for this->state.use_rect_clipping() to be false, this->state.clipping_path must be non-empty, but that appears to be an unimplemented feature in kiva's AGG backend. this->state.clipping_path is always empty: https://github.com/enthought/enable/blob/ccb76800a11f1d0d2f6618bbf6a6498839ed62cb/kiva/agg/src/kiva_graphics_context.h#L787-L789

I suspect, but do not know, that bad values are being passed as the clipping rect and that big block of code is throwing an exception somewhere.

jwiggins avatar Apr 06 '23 09:04 jwiggins

One thing to try is to see what happens when using a backend other than old Agg. Something like:

ETS_TOOLKIT=qt.qpainter python -X faulthandler enable/examples/demo/savage/buttons_on_canvas.py

or

ETS_TOOLKIT=qt.celiagg python -X faulthandler enable/examples/demo/savage/buttons_on_canvas.py

corranwebster avatar Apr 06 '23 11:04 corranwebster

ETS_TOOLKIT=qt.qpainter python -X faulthandler enable/examples/demo/savage/buttons_on_canvas.py

After test, I found qt.qpainter can run with no error.

But the qt.celiagg will yield the following error:

Traceback (most recent call last):
  File "/Users/cyliu/Documents/3.11_test/enable/enable/examples/demo/savage/buttons_on_canvas.py", line 23, in <module>
    from enable.savage.svg.backends.kiva.renderer import Renderer as KivaRenderer
  File "/Users/cyliu/.venvs/py311/lib/python3.11/site-packages/enable/savage/svg/backends/kiva/renderer.py", line 36, in <module>
    class CompiledPath(KivaCompiledPath):
  File "/Users/cyliu/.venvs/py311/lib/python3.11/site-packages/enable/savage/svg/backends/kiva/renderer.py", line 38, in CompiledPath
    AddPath = KivaCompiledPath.add_path
              ^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: type object 'Unimplemented' has no attribute 'add_path

homosapien-lcy avatar Apr 07 '23 04:04 homosapien-lcy

After more tests:

        if sys.platform == "darwin":
            print('in darwin')
            if self.spreadMethod != "pad":
                warnings.warn(
                    "spreadMethod %r is not supported. Using 'pad'"
                    % self.spreadMethod
                )

            if bbox is not None:
                print("in clip")
                print("bbox", bbox)
                gc.clip_to_rect(*bbox)

The bbox inputted that caused unimplemented error seems normal:

in darwin
in clip
bbox [0.0, 0.0, 72.0, 16.0]
in darwin
in clip
bbox [0.0, 0.0, 104.00238, 120.0]
in darwin
in clip
bbox [0.0, 0.0, 77.351562, 25.5921935]
in darwin
in clip
bbox [0.0, 0.0, 84.556527, 32.353364]
libc++abi: terminating with uncaught exception of type kiva::$_0
Fatal Python error: Aborted

Indeed, even if I pin the bbox value to all 0s (enable/savage/svg/backends/kiva/renderer.py, line 195-235)

    def set_on_gc(self, gc, bbox=None):

        # Apply transforms
        if self.transforms is not None:
            for func, f_args in self.transforms:
                if isinstance(f_args, tuple):
                    func(gc, *f_args)
                else:
                    func(gc, f_args)

        x1 = self.x1
        x2 = self.x2
        y1 = self.y1
        y2 = self.y2

        if sys.platform == "darwin":
            if self.spreadMethod != "pad":
                warnings.warn(
                    "spreadMethod %r is not supported. Using 'pad'"
                    % self.spreadMethod
                )

            if bbox is not None:
                bbox = [0, 0, 0, 0]
                gc.clip_to_rect(*bbox)
            else:
                print("Alert, None!!!")

        else:
            if self.units == "objectBoundingBox" and bbox is not None:
                x1 = (bbox[2] + bbox[0]) * x1
                y1 = (bbox[3] + bbox[1]) * y1
                x2 = (bbox[2] + bbox[0]) * x2
                y2 = (bbox[3] + bbox[1]) * y2

                self.bbox_transform(gc, bbox)

        stops = np.transpose(self.stops)
        gc.linear_gradient(
            x1, y1, x2, y2, stops, self.spreadMethod, self.units
        )

I print the *args out before it is past into the C++ code

    def clip_to_rect(self, *args):
        print("in clip_to_rect", *args)
        return _agg.GraphicsContextArray_clip_to_rect(self, *args)

The program will still failed in the end, even though it didn't raised error with the same input in the first few calls:

2023-04-10 14:45:13.649 Python[7063:125720] ApplePersistenceIgnoreState: Existing state will not be touched. New state will be written to /var/folders/2z/kylzj9s92y71cxscmljmpqrh0000gt/T/org.python.python.savedState
in clip_to_rect 0.0 0.0 108.0 120.0
in clip_to_rect 0.0 0.0 106.25 118.28572
in clip_to_rect 0 0 0 0
in clip_to_rect 0.0 0.0 108.0 120.0
in clip_to_rect 0.0 0.0 106.25 118.28572
in clip_to_rect 0 0 0 0
in clip_to_rect 0 0 0 0
in clip_to_rect 0 0 0 0
in clip_to_rect 0 0 0 0
in clip_to_rect 0 0 0 0
in clip_to_rect 0 0 0 0
in clip_to_rect 0 0 0 0
libc++abi: terminating with uncaught exception of type kiva::$_0
Fatal Python error: Aborted

Current thread 0x0000000109f83600 (most recent call first):
  File "/Users/cyliu/.venvs/py311_test_2/lib/python3.11/site-packages/kiva/agg/agg.py", line 1184 in clip_to_rect
  File "/Users/cyliu/.venvs/py311_test_2/lib/python3.11/site-packages/enable/savage/svg/backends/kiva/renderer.py", line 219 in set_on_gc
  File "/Users/cyliu/.venvs/py311_test_2/lib/python3.11/site-packages/enable/savage/svg/backends/kiva/renderer.py", line 537 in gradientPath
  File "/Users/cyliu/.venvs/py311_test_2/lib/python3.11/site-packages/enable/savage/svg/document.py", line 1119 in render
  File "/Users/cyliu/Documents/3.11_test_2/enable/enable/examples/demo/savage/buttons_on_canvas.py", line 79 in _draw_svg_document
  File "/Users/cyliu/Documents/3.11_test_2/enable/enable/examples/demo/savage/buttons_on_canvas.py", line 59 in draw
  File "/Users/cyliu/Documents/3.11_test_2/enable/enable/examples/demo/savage/buttons_on_canvas.py", line 99 in draw
  File "/Users/cyliu/.venvs/py311_test_2/lib/python3.11/site-packages/enable/abstract_window.py", line 536 in _paint
  File "/Users/cyliu/.venvs/py311_test_2/lib/python3.11/site-packages/enable/qt4/base_window.py", line 90 in paintEvent
  File "/Users/cyliu/.venvs/py311_test_2/lib/python3.11/site-packages/enable/qt4/base_window.py", line 255 in paintEvent
  File "/Users/cyliu/.venvs/py311_test_2/lib/python3.11/site-packages/pyface/util/guisupport.py", line 156 in start_event_loop_qt4
  File "/Users/cyliu/.venvs/py311_test_2/lib/python3.11/site-packages/traitsui/qt4/view_application.py", line 138 in __init__
  File "/Users/cyliu/.venvs/py311_test_2/lib/python3.11/site-packages/traitsui/qt4/view_application.py", line 92 in view_application
  File "/Users/cyliu/.venvs/py311_test_2/lib/python3.11/site-packages/traitsui/qt4/toolkit.py", line 237 in view_application
  File "/Users/cyliu/.venvs/py311_test_2/lib/python3.11/site-packages/traits/has_traits.py", line 2164 in configure_traits
  File "/Users/cyliu/Documents/3.11_test_2/enable/enable/examples/demo/savage/buttons_on_canvas.py", line 181 in <module>

Extension modules: traits.ctraits, numpy.core._multiarray_umath, numpy.core._multiarray_tests, numpy.linalg._umath_linalg, numpy.fft._pocketfft_internal, numpy.random._common, numpy.random.bit_generator, numpy.random._bounded_integers, numpy.random._mt19937, numpy.random.mtrand, numpy.random._philox, numpy.random._pcg64, numpy.random._sfc64, numpy.random._generator, kiva._cython_speedups, kiva._marker_renderer, xxsubtype, shiboken6.Shiboken, PySide6.QtCore, PySide6.QtGui, PySide6.QtWidgets, PySide6.QtPrintSupport, PySide6.QtNetwork, PySide6.QtWebChannel, PySide6.QtWebEngineCore, PySide6.QtWebEngineWidgets, kiva.agg._agg, kiva.agg._plat_support, PySide6.QtOpenGL, PySide6.QtOpenGLWidgets, kiwisolver._cext, PySide6.QtSvg, PySide6.QtSvgWidgets (total: 33)
zsh: abort      python3.11 -X faulthandler enable/examples/demo/savage/buttons_on_canvas.py

homosapien-lcy avatar Apr 10 '23 05:04 homosapien-lcy

I tested it with edm on Python 3.8 and Linux (enable 5.3) and it works as expected. The issue seems to be OS-X specific.

dpinte avatar Apr 10 '23 08:04 dpinte