-
Notifications
You must be signed in to change notification settings - Fork 24
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
Replace use of sweet_pickle in naming, and delete sweet_pickle subpackage #199
Conversation
…n apptools.naming to use apptools.persistence or pickle from the standard library instead
…ools into kill-sweet-pickle
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Persistence is tricky because we also need to think about files that have been created in the past. Although pickling is not meant for long-term persistence, we still need to think about them...
Looks like Mayavi uses of apptools.naming (which uses sweet_pickle
) is mostly transient: It is about binding variables to the Python shell.
As for the changes in apptools.naming.object_serializer
, it seems those lines are indirectly covered by tests exercising apptools.naming.pyfs_context
or apptools.naming.context._bind
, and I can't find a test to ensure that round-tripping is working correctly, i.e. what is saved by ObjectSerializer.save
can be loaded by ObjectSerializer.load
correctly.
So I added the following test, which passes on master:
New test for roundtripping
# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
# All rights reserved.
#
# This software is provided without warranty under the terms of the BSD
# license included in LICENSE.txt and may be redistributed only under
# the conditions described in the aforementioned license. The license
# is also available online at http://www.enthought.com/licenses/BSD.txt
#
# Thanks for using Enthought open source!
import os
import shutil
import tempfile
import unittest
from traits.api import cached_property, HasTraits, Property, Str, Event
from apptools.naming.api import ObjectSerializer
class FooWithTraits(HasTraits):
""" Dummy HasTraits class for testing ObjectSerizalizer."""
full_name = Str()
last_name = Property(depends_on="full_name")
event = Event()
@cached_property
def _get_last_name(self):
return self.full_name.split(" ")[-1]
class TestObjectSerializer(unittest.TestCase):
def setUp(self):
self.tmpdir = tempfile.mkdtemp()
self.addCleanup(shutil.rmtree, self.tmpdir)
self.tmp_file = os.path.join(self.tmpdir, "tmp.pickle")
def test_save_load_roundtrip(self):
# Test HasTraits objects can be serialized and deserialized as expected
obj = FooWithTraits(full_name="John Doe")
serializer = ObjectSerializer()
serializer.save(self.tmp_file, obj)
self.assertTrue(serializer.can_load(self.tmp_file))
deserialized = serializer.load(self.tmp_file)
self.assertIsInstance(deserialized, FooWithTraits)
self.assertEqual(deserialized.full_name, "John Doe")
self.assertEqual(deserialized.last_name, "Doe")
But it fails on this branch:
======================================================================
ERROR: test_save_load_roundtrip (apptools.naming.tests.test_object_serializer.TestObjectSerializer)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/Users/kchoi/Work/ETS/apptools/apptools/naming/tests/test_object_serializer.py", line 48, in test_save_load_roundtrip
deserialized = serializer.load(self.tmp_file)
File "/Users/kchoi/Work/ETS/apptools/apptools/naming/object_serializer.py", line 58, in load
obj = VersionedUnpickler(f).load()
File "/Users/kchoi/Work/ETS/apptools/apptools/persistence/versioned_unpickler.py", line 37, in load
ret = Unpickler.load(self)
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/pickle.py", line 1210, in load
dispatch[key[0]](self)
TypeError: 'classmethod' object is not callable
Something still needs fixing?
Note there is this integration test for VersionedUnpickler: https://github.com/enthought/apptools/blob/master/integrationtests/persistence/test_persistence.py (I'm simply linking this because I saw it existed, it is very possibly irrelevant / we may just want to delete it) |
So, I was able to get this test (and all the tests) to pass on this branch, by simply removing the apptools/apptools/persistence/versioned_unpickler.py Lines 93 to 104 in a693c79
It appears this code was copy pasted from |
I think that's because this line wasn't removed after
This line is equivalent to adding |
@aaronayres35 The roundtripping test above is quite useful for making sure the changes in this PR has not broken object serializer (in the most trivial ways at least). I think it would be good to add the test as part of this PR, it will also serve as future reference as to what extent the object serializer remains to work. I saw that you have removed |
Yeah that makes sense, I will add the test to this PR and have it sit in
Somehow when merging / addressing conflicts I must have made a mistake and readded |
I see! That's probably why I did not see them when I checked out the branch and now they reappear.
Adding them to |
…pickle and remove test_global registry
Note |
Some of the In fact, I am not confident that |
I see. Let's try to convert the tests that can be converted. Then we can more confidently say which functionality can be migrated and which are being discarded. |
def test_infinite_loop_detection(self): | ||
"""Validates that the class mapping framework detects infinite | ||
loops of class mappings. | ||
""" | ||
# Add mappings to the registry | ||
self.registry.add_mapping_to_class(Foo.__module__, Foo.__name__, Bar) | ||
self.registry.add_mapping_to_class(Bar.__module__, Bar.__name__, Baz) | ||
self.registry.add_mapping_to_class(Baz.__module__, Baz.__name__, Foo) | ||
|
||
# Validate that an exception is raised when trying to unpickle an | ||
# instance anywhere within the circular definition. | ||
def fn(o): | ||
sweet_pickle.loads(sweet_pickle.dumps(o)) | ||
|
||
self.assertRaises(sweet_pickle.UnpicklingError, fn, Foo()) | ||
self.assertRaises(sweet_pickle.UnpicklingError, fn, Bar()) | ||
self.assertRaises(sweet_pickle.UnpicklingError, fn, Baz()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe cycles aren't possible with how persistence
is set up, so I deleted this test. The following test I modified slightly as there is only one transition that can occur. ie with persistence you can't go Foo->Bar->Baz in one roundtrip
def test_unpickled_chain_functionality(self): | ||
"""Validates that the registered state functions are used when | ||
unpickling. | ||
""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe it may be possible to modify this test, to get a similar flavor of test for apptools.persistence
by using the setstates
attribute on an Updater
instance that is passed to a VersionedUnpickler
. Unfortunately I was unable to get it to work atm.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I looked into this briefly... IMO, it looks evil! I opened #238.
Orthogonal to this PR, but I think apptools could really benefit from improved docstrings (numpy style). Obviously documentation could be improved in general, but I find myself spending a lot of time just trying to understand what arguments are, etc. For example when workng on this PR I noticed that the apptools/apptools/persistence/updater.py Lines 9 to 36 in d794275
It is however used here:
I discovered it here: apptools/integrationtests/persistence/update1.py Lines 39 to 41 in d794275
docstrings would save a lot of time spent trying to follow the code |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you very much for converting the tests, it really helps us to understand what can or cannot be migrated!
Some suggestions for the tests and the news fragment given the new information. The rest looks good to me.
def test_unpickled_chain_functionality(self): | ||
"""Validates that the registered state functions are used when | ||
unpickling. | ||
""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I looked into this briefly... IMO, it looks evil! I opened #238.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
fixes #133
This PR very possibly is biting the bullet too early on this, so I am happy to hold off on merging or close if need be. (we might not want to kill
sweet_pickle
!)sweet_pickle
was previously only used in a very minor way inapptools.naming
, and was not used anywhere else inapptools
. Additionally, AFAIK it is not used in other projects currently (this might not be the case in which case we can simply close this PR). This PR replaces its use inapptools.naming
and deletes thesweet_pickle
subpackage.For more detailed commentary on this see #133 and specifically these comments: #133 (comment) #133 (comment)
It was also mentioned in the EEP that in
envisage
"Thesingle_project
plugin usessweet_pickle
andnaming
.". However,single_project
was removed by enthought/envisage#331Checklist
Also, we could do a quick scream test on slack like we did for
apptools.lru_cache
.