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

Can the expression engine provide context for errors somehow? #493

Open
frmdstryr opened this issue May 14, 2022 · 3 comments
Open

Can the expression engine provide context for errors somehow? #493

frmdstryr opened this issue May 14, 2022 · 3 comments

Comments

@frmdstryr
Copy link
Contributor

If an error occurs in an expression during the initialization it provides very little context as to where the error is making it difficult to debug in more complex applications.

For example this script:

from enaml.core.api import Conditional
from enaml.widgets.api import Window, Container, Label


enamldef Main(Window):
    Container:
        Conditional:
            condition = None
            Label:
                text = "Hello"

Will generate this error:

Traceback (most recent call last):
  File "/home/user/micromamba/envs/enaml/bin/enaml-run", line 33, in <module>
    sys.exit(load_entry_point('enaml', 'console_scripts', 'enaml-run')())
  File "/home/user/projects/enaml/enaml/runner.py", line 71, in main
    window.show()
  File "/home/user/projects/enaml/enaml/widgets/window.py", line 380, in show
    self.initialize()
  File "/home/user/projects/enaml/enaml/widgets/window.py", line 158, in initialize
    super(Window, self).initialize()
  File "/home/user/projects/enaml/enaml/widgets/toolkit_object.py", line 160, in initialize
    super(ToolkitObject, self).initialize()
  File "/home/user/projects/enaml/enaml/core/declarative.py", line 135, in initialize
    child.initialize()
  File "/home/user/projects/enaml/enaml/widgets/toolkit_object.py", line 160, in initialize
    super(ToolkitObject, self).initialize()
  File "/home/user/projects/enaml/enaml/core/declarative.py", line 135, in initialize
    child.initialize()
  File "/home/user/projects/enaml/enaml/core/pattern.py", line 38, in initialize
    self.refresh_items()
  File "/home/user/projects/enaml/enaml/core/conditional.py", line 74, in refresh_items
    if self.condition:
TypeError: The 'condition' member on the 'Conditional' object must be of type 'bool'. Got object of type 'NoneType' instead.

The trace shows the initialization stack but no context as to where the Conditional is in the tree. Since the Conditional is an instance of a node in the tree can you think of any way to provide some sort of context?

One thought that comes to mind is having declarative do child initialization in a try except and do some sort of inspection?

@MatthieuDartiailh
Copy link
Member

Error within declarative widgets are indeed an area where we can improve much. In this specific case, having some try except in initialization may be the best solution since the error is not in the declarative part but simply in the validation of the member. However we could also try to improve error reported in the expression engine for cases when the error occurs at runtime and not at initialization time.

I do not have a plan to address this (and right now it is not my top priority) so feel free to investigate if you have time to do so.

As a side note, I am considering switching the Bool member for a Coerced bool in widgets because having to explicitly cast to bool feels very unpythonic.

@tstordyallison
Copy link

+1

I've played around with this sort of thing a few times in the past, but never quite had the time to put something together that was comprehensive.

As you point out - there's a few different points we'd want to better report the 'declarative stack' as we encounter an error. activation/init being one of them, but also at runtime when for whatever reason we fail to update the state managed by a proxy.

@frmdstryr
Copy link
Contributor Author

I pushed a branch https://github.com/nucleic/enaml/compare/main...frmdstryr:declarative-context?expand=1 which helps with the initialization. Would be nice to be able to add lines into the normal traceback if anyone knows how to do that. I'm not sure what to think about saving the fileno/line for each node cost wise, will have to do some testing.

Eg

from enaml.core.api import Conditional
from enaml.widgets.api import Window, Container, Label, PushButton


enamldef MyContainer(Container): inside_my_container:
    Label: my_label:
        text = "Hello"
    Conditional: cond:
        condition = None
        PushButton: btn:
            text = "Blow up"
            clicked :: cond.condition = None

enamldef Main(Window): main:
    Container: container:
        Label: label1:
            text = "Test"
        MyContainer: my_container:
            pass

Will produce this:

Traceback (most recent call last):
  File "/home/user/projects/enaml/enaml/core/declarative.py", line 185, in initialize
    child.initialize()
  File "/home/user/projects/enaml/enaml/core/pattern.py", line 38, in initialize
    self.refresh_items()
  File "/home/user/projects/enaml/enaml/core/conditional.py", line 74, in refresh_items
    if self.condition:
TypeError: The 'condition' member on the 'Conditional' object must be of type 'bool'. Got object of type 'NoneType' instead.

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/home/user/micromamba/envs/enaml/bin/enaml-run", line 33, in <module>
    sys.exit(load_entry_point('enaml', 'console_scripts', 'enaml-run')())
  File "/home/user/projects/enaml/enaml/runner.py", line 71, in main
    window.show()
  File "/home/user/projects/enaml/enaml/widgets/window.py", line 380, in show
    self.initialize()
  File "/home/user/projects/enaml/enaml/widgets/window.py", line 158, in initialize
    super(Window, self).initialize()
  File "/home/user/projects/enaml/enaml/widgets/toolkit_object.py", line 160, in initialize
    super(ToolkitObject, self).initialize()
  File "/home/user/projects/enaml/enaml/core/declarative.py", line 185, in initialize
    child.initialize()
  File "/home/user/projects/enaml/enaml/widgets/toolkit_object.py", line 160, in initialize
    super(ToolkitObject, self).initialize()
  File "/home/user/projects/enaml/enaml/core/declarative.py", line 185, in initialize
    child.initialize()
  File "/home/user/projects/enaml/enaml/widgets/toolkit_object.py", line 160, in initialize
    super(ToolkitObject, self).initialize()
  File "/home/user/projects/enaml/enaml/core/declarative.py", line 189, in initialize
    raise InitializationError(child, e) from e
enaml.core.declarative.InitializationError: The 'condition' member on the 'Conditional' object must be of type 'bool'. Got object of type 'NoneType' instead.
  File "tests/example.enaml", line 14, in Main
  File "tests/example.enaml", line 15, in Container
  File "tests/example.enaml", line 18, in MyContainer
  File "tests/example.enaml", line 8, in Conditional

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

No branches or pull requests

3 participants