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

Deal with __getstate__ methods #633

Merged
merged 20 commits into from
Apr 15, 2021
Merged

Conversation

aaronayres35
Copy link
Contributor

@aaronayres35 aaronayres35 commented Apr 7, 2021

closes #583, #248

This PR rermoves uses of the __getstate__ method when it is not needed, and cleans up its use where it is.

The __setstate__ methods can be dealt with in a follow up PR

@@ -49,13 +49,6 @@ def map_data_array(self, screen_vals):
# ------------------------------------------------------------------------
# Persistence-related methods
# ------------------------------------------------------------------------
def __getstate__(self):
state = super(AbstractMapper, self).__getstate__()
for key in ["_cache_valid"]:
Copy link
Contributor Author

Choose a reason for hiding this comment

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

_cache_valid is not a trait on this class or any of its base classes

Copy link
Contributor

@rahulporuri rahulporuri Apr 13, 2021

Choose a reason for hiding this comment

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

sigh. but it is a trait on the subclasses e.g. Base1DMapper so those need to be updated

Copy link
Contributor Author

@aaronayres35 aaronayres35 Apr 13, 2021

Choose a reason for hiding this comment

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

Ah you are right. TBH I believe that all related traits should be transient even if they weren't perviously involved in a __getstate__method. For example other classes have _screen_cache_valid or _selection_cache_valid traits. I think I will update all of these (to be specific I am going to update any privately named cache trait) to be transient as I can't see why they shouldn't all be so

It also doesn't make sense that for some objects the _cache_valid and similar traits would be transient traits but on others not


# -------------------------------------------------------------------------
# Appearance properties (for Box mode)
# -------------------------------------------------------------------------

# The pointer to use when drawing a zoom box.
pointer = "magnifier"
pointer = Str("magnifier", transient=True)
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 may be bad if pointer is meant to be a class attribute, similar to stack_index in plot_container.py.

I need to climb up the class hierarchy to see if it was previously defined as a trait. I remember Kit mentioning this being a point of confusion. I am not sure what the ideal way to avoid this confusion would be (aside from maybe a comment above saying if this is a class attribute or just setting a trait to a value. I guess if setting a trait to some value, you could do as I have done here? and explicitly do something like Str(___)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

As a quick sanity check, on master I defined a class attribute thing = "A" on SimpleZoom. I then ran:

from chaco.tools.simple_zoom import SimpleZoom
SimpleZoom.thing  # I see 'A' printed
SimpleZoom.pointer  # I get an error "type object 'SimpleZoom' has no attribute 'pointer'"

So, I believe the change made here is the right one

@@ -247,7 +254,7 @@ def test_serialization_state_no_persist(self):
"_min_index",
"_max_index",
]:
self.assertIn(key, state)
self.assertNotIn(key, state)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

If not persisting they should not be in state, but if we are they should be. I believe these tests are doing what they are supposed to now

@aaronayres35 aaronayres35 changed the title [WIP] Deal with __getstate__ and __setstate__ methods Deal with __getstate__ and __setstate__ methods Apr 9, 2021
if key in state:
del state[key]
if "stack_index" in state:
del state["stack_index"]
Copy link
Contributor Author

Choose a reason for hiding this comment

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

stack_index is a class attribute not a trait, so I believe this is still needed

@aaronayres35 aaronayres35 requested a review from rahulporuri April 9, 2021 15:14
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 with a couple of comments

@@ -49,13 +49,6 @@ def map_data_array(self, screen_vals):
# ------------------------------------------------------------------------
# Persistence-related methods
# ------------------------------------------------------------------------
def __getstate__(self):
state = super(AbstractMapper, self).__getstate__()
for key in ["_cache_valid"]:
Copy link
Contributor

@rahulporuri rahulporuri Apr 13, 2021

Choose a reason for hiding this comment

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

sigh. but it is a trait on the subclasses e.g. Base1DMapper so those need to be updated

@@ -449,14 +449,6 @@ def _line_width_changed(self):
self.invalidate_draw()
self.request_redraw()

def __getstate__(self):
state = super(LinePlot, self).__getstate__()
for key in ["traits_view"]:
Copy link
Contributor

Choose a reason for hiding this comment

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

Is traits_view a transient trait? I dont see anything in the code or in the documentation saying that it is.

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 was going off wha Corran said in this issue description: "This is removing the traits_view trait from the state - under normal Traits, this will never be in the state because of special handling of Views, so this whole method can be removed."
This should certainly be documented though

Comment on lines -661 to -663
"reset_zoom_key",
"prev_zoom_key",
"next_zoom_key",
Copy link
Contributor

Choose a reason for hiding this comment

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

im not sure where these traits were being magically defined

Copy link
Contributor Author

Choose a reason for hiding this comment

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

me either, this is the only place I was seeing them in the codebase. Perhaps they once existed but had been removed

@aaronayres35
Copy link
Contributor Author

Looks like the CI failure on windows/pyqt from the test introduced by #636 is reproducible on every PR except the original (no idea how it didn't fail on 636).

======================================================================
ERROR: test_dont_crash_on_click (chaco.tests.test_plot.TestEmptyPlot)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "D:\a\chaco\chaco\.edm\envs\chaco-test-3.6-pyqt\lib\site-packages\chaco\tests\test_plot.py", line 134, in test_dont_crash_on_click
    [(1, 1), (25, 25), (50, 50), (100, 100)],
  File "D:\a\chaco\chaco\.edm\envs\chaco-test-3.6-pyqt\lib\contextlib.py", line 88, in __exit__
    next(self.gen)
  File "D:\a\chaco\chaco\.edm\envs\chaco-test-3.6-pyqt\lib\site-packages\traitsui\testing\tester\ui_tester.py", line 106, in create_ui
    process_cascade_events()
  File "D:\a\chaco\chaco\.edm\envs\chaco-test-3.6-pyqt\lib\contextlib.py", line 88, in __exit__
    next(self.gen)
  File "D:\a\chaco\chaco\.edm\envs\chaco-test-3.6-pyqt\lib\site-packages\traitsui\testing\_exception_handling.py", line 74, in reraise_exceptions
    raise RuntimeError(msg)
RuntimeError: Uncaught exceptions found.
=== Exception (type: <class 'AttributeError'>, value: '_QtWindow' object has no attribute 'handler') ===
Traceback (most recent call last):
  File "D:\a\chaco\chaco\.edm\envs\chaco-test-3.6-pyqt\lib\site-packages\enable\qt4\base_window.py", line 258, in leaveEvent
    self.handler.leaveEvent(event)
AttributeError: '_QtWindow' object has no attribute 'handler'

I also find the error quite confusing, as looking in enable, the _QtWindow class clearly has self.handler = _QtWindowHandler(self, enable_window) in its __init__ method... Also this seems like a very strange thing to be windows specific... 🤔
I will look into this more


# Is the cached color data valid?
_colors_cache_valid = Bool(False)
_colors_cache_valid = Bool(False, transient=True)
Copy link
Contributor

Choose a reason for hiding this comment

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

If the cache valid flags arent transient, then the caches themselves should also be transient - _levels and _colors.

I would think HasTraits would ignore traits that are private i.e. that start an _. If that isn't the case, there are a lot more private traits that we will want to mark as transient.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

As you suggested, I printed state inside the ArrayDataSource.__getstate__ method on master and ran the test suite. I do see _ prefixed traits, eg _data, '_min_index', etc. So it looks like these need to be addressed

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If that isn't the case, there are a lot more private traits that we will want to mark as transient.

Does this mean all private traits be transient?

Copy link
Contributor

Choose a reason for hiding this comment

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

As you suggested, I printed state inside the ArrayDataSource.getstate method on master and ran the test suite. I do see _ prefixed traits, eg _data, '_min_index', etc. So it looks like these need to be addressed

Sigh. I was afraid of that.

Does this mean all private traits be transient?

Let's not do that. Definitely not in this PR.

Copy link
Contributor

Choose a reason for hiding this comment

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

So, I imagine the code will recreate the cache if a *cache* is False, which it is by default because we're marking it as a transient. So, I think we can ignore all of the traits that are recalculated based on the *cache* flag. - i.e. nothing actionable in this PR.

Comment on lines 60 to 61
# Cached list of line widths
_widths = List
Copy link
Contributor

Choose a reason for hiding this comment

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

looks like this is technically a cache but like I said earlier, this will be ignored/recomputed because the _widths_cache_valid trait is False - no action required, just pointing it out.

chaco/tools/simple_zoom.py Outdated Show resolved Hide resolved
chaco/tools/simple_zoom.py Outdated Show resolved Hide resolved
Comment on lines 41 to 42
# Is the tool always "on"? If True, left-clicking always initiates
# a zoom operation; if False, the user must press a key to enter zoom mode.
Copy link
Contributor

Choose a reason for hiding this comment

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

looks like you lost the #: because of the merge conflicts.

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 df05047 into master Apr 15, 2021
@aaronayres35 aaronayres35 deleted the audit-dunder-state-methods branch April 15, 2021 16:18
@aaronayres35 aaronayres35 changed the title Deal with __getstate__ and __setstate__ methods Deal with __getstate__ methods Apr 15, 2021
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.

Audit __getstate__ and __setstate__ methods
2 participants