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

Convert on_trait_change decorators to observe #1519

Merged
merged 22 commits into from
Feb 23, 2021

Conversation

aaronayres35
Copy link
Contributor

@aaronayres35 aaronayres35 commented Feb 11, 2021

contributes to #1052

This PR replaces all uses of @on_trait_change with the equivalent form using observe

Comment on lines 138 to 145
@observe('guests.items')
def _guests_modified(self, event):
if isinstance(event.object, Hotel):
for guest in event.new:
guest.hotel = self
else:
for guest in event.added:
guest.hotel = self
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is similar to the commit immediately prior. It is unclear me how we want to handle cases where we were listening to container[] and now observe container.items, but the handler previously expeced to have added and removed. Changes to container will fire a TraitChangeEvent which has object, name, old, new, whereas changes to an item in the container will have a TraitListEvent or the like which allows us to access added/removed. Also this example currently is broken with the following error when you click "add guest":

ERROR:traits:Exception occurred in traits notification handler for object: <__main__.Hotel object at 0x7f8a298bb570>, trait: guests_items, old value: <undefined>, new value: TraitListEvent(index=4, removed=[], added=[<__main__.Guest object at 0x7f8a29a650f8>])
Traceback (most recent call last):
  File "/Users/aayres/.edm/envs/traitsui-test-3.6-pyqt5/lib/python3.6/site-packages/traits/trait_notifiers.py", line 524, in _dispatch_change_event
    self.dispatch(handler, *args)
  File "/Users/aayres/.edm/envs/traitsui-test-3.6-pyqt5/lib/python3.6/site-packages/traits/trait_notifiers.py", line 619, in dispatch
    handler(*args)
  File "/Users/aayres/.edm/envs/traitsui-test-3.6-pyqt5/lib/python3.6/site-packages/traitsui/qt4/list_editor.py", line 559, in update_editor_item
    ui, view_object, monitoring = self._create_page(object)
  File "/Users/aayres/.edm/envs/traitsui-test-3.6-pyqt5/lib/python3.6/site-packages/traitsui/qt4/list_editor.py", line 657, in _create_page
    parent=self.control, view=factory.view, kind=factory.ui_kind
  File "/Users/aayres/.edm/envs/traitsui-test-3.6-pyqt5/lib/python3.6/site-packages/traits/has_traits.py", line 1744, in edit_traits
    args,
  File "/Users/aayres/.edm/envs/traitsui-test-3.6-pyqt5/lib/python3.6/site-packages/traitsui/view.py", line 455, in ui
    ui.ui(parent, kind)
  File "/Users/aayres/.edm/envs/traitsui-test-3.6-pyqt5/lib/python3.6/site-packages/traitsui/ui.py", line 237, in ui
    self.rebuild(self, parent)
  File "/Users/aayres/.edm/envs/traitsui-test-3.6-pyqt5/lib/python3.6/site-packages/traitsui/qt4/toolkit.py", line 157, in ui_subpanel
    ui_panel.ui_subpanel(ui, parent)
  File "/Users/aayres/.edm/envs/traitsui-test-3.6-pyqt5/lib/python3.6/site-packages/traitsui/qt4/ui_panel.py", line 72, in ui_subpanel
    _ui_panel_for(ui, parent, True)
  File "/Users/aayres/.edm/envs/traitsui-test-3.6-pyqt5/lib/python3.6/site-packages/traitsui/qt4/ui_panel.py", line 78, in _ui_panel_for
    ui.control = control = _Panel(ui, parent, is_subpanel).control
  File "/Users/aayres/.edm/envs/traitsui-test-3.6-pyqt5/lib/python3.6/site-packages/traitsui/qt4/ui_panel.py", line 140, in __init__
    self.control = panel(ui)
  File "/Users/aayres/.edm/envs/traitsui-test-3.6-pyqt5/lib/python3.6/site-packages/traitsui/qt4/ui_panel.py", line 269, in panel
    panel = _GroupPanel(content[0], ui).control
  File "/Users/aayres/.edm/envs/traitsui-test-3.6-pyqt5/lib/python3.6/site-packages/traitsui/qt4/ui_panel.py", line 627, in __init__
    layout = self._add_items(content, inner)
  File "/Users/aayres/.edm/envs/traitsui-test-3.6-pyqt5/lib/python3.6/site-packages/traitsui/qt4/ui_panel.py", line 893, in _add_items
    ui, object, name, item.tooltip, None
  File "/Users/aayres/.edm/envs/traitsui-test-3.6-pyqt5/lib/python3.6/site-packages/traitsui/editors/range_editor.py", line 283, in simple_editor
    ui
  File "/Users/aayres/.edm/envs/traitsui-test-3.6-pyqt5/lib/python3.6/site-packages/traitsui/editors/range_editor.py", line 193, in _get_low_high
    low = self.named_value(self.low_name, ui)
  File "/Users/aayres/.edm/envs/traitsui-test-3.6-pyqt5/lib/python3.6/site-packages/traitsui/editor_factory.py", line 108, in named_value
    value = getattr(value, name)
AttributeError: 'NoneType' object has no attribute 'min_temperature'

This seems to originate for the _add_guest_changed method below.

We should not merge this PR until this is fixed

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if it's better to split the listener into two separate methods - one for guests and another for guests:items - this way, we can handle things cleanly.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this question should also be discussed on the internal slack channel - so that we can settle on a consensus for what the right thing to do is. IMO, the best thing is to separate the listeners.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will post a question shortly after I go through the other suggested changes

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this specific piece of code can stay as is. We don't need to separate it into two methods - which listen to guests vs guests:items. But, i think it's definitely useful to add a comment differentiating the two branches - it took me a little time to understand which branch handled what.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will add a comment. I am unsure exactly where the failure is coming from. It seems to originate in _add_guest_changed not the _guests_modified method that we changed here. However, this error is not observed (at least loudly) without these changes. It is possible something had been broken perviously but on trait change didn't make a fuss about it

traitsui/editors/image_enum_editor.py Outdated Show resolved Hide resolved
traitsui/editors/image_enum_editor.py Outdated Show resolved Hide resolved
traitsui/qt4/datetime_editor.py Outdated Show resolved Hide resolved
traitsui/qt4/datetime_editor.py Outdated Show resolved Hide resolved
traitsui/qt4/datetime_editor.py Outdated Show resolved Hide resolved
traitsui/wx/code_editor.py Outdated Show resolved Hide resolved
traitsui/wx/code_editor.py Outdated Show resolved Hide resolved
traitsui/wx/scrubber_editor.py Outdated Show resolved Hide resolved
traitsui/wx/scrubber_editor.py Outdated Show resolved Hide resolved
Co-authored-by: Poruri Sai Rahul <rporuri@enthought.com>
Comment on lines 261 to 266
if isinstance(event.object, KeyBinding):
for item in event.new:
item.parent = self
else:
for item in event.added:
item.parent = self
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same comment here - a short comment explaining the difference between the two branches would be useful here i think.


def _add_guest_changed(self):
self.guests.append(Guest())
self.guests.append(Guest(hotel=self))
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this fixes the previous error, but now it feels like listening to items above is pointless.... As I believe (for this demos sake) the only way guests.items will be changed is via the add_guest button.
The cause of this issue is still mysterious to me. It seems that when the Guest object is instantiated, we have the following trait definitions that need to be sorted out:

# The maximum temperature allowed by the guest's plan:
    max_temperature = Property(depends_on='plan')

    # The current room temperature as set by the guest:
    temperature = Range('hotel.min_temperature', 'max_temperature')

however, at the time of instantiation the hotel is not defined (when we have the observe decorated listener above, but with on trait change before this worked fine).

To prevent the error mentioned #1519 (comment) I simply specify the hotel to be self at the time we try to create the Guest object. But that makes the need to set guest.hotel = self above completely redundant...

This all feels somewhat fundamental to the purpose of the demo, but I don't think my understanding of the the dynamic range is entirely there to fully make sense of this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rahulporuri shall I remove the code for listening to items in the above listener now? Seems strange this example worked before but not with observe without this change on line 150

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'm not sure it's worth the effort figuring out what the best way is to structure this example. let's leave it as is for now.

Copy link
Contributor

@rahulporuri rahulporuri left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@aaronayres35 aaronayres35 merged commit 14ef03b into master Feb 23, 2021
@aaronayres35 aaronayres35 deleted the otc-to-observe-decorators branch February 23, 2021 13:27
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 this pull request may close these issues.

None yet

2 participants