Skip to content

Conversation

@mdickinson
Copy link
Member

This PR makes traits of type Instance, Type and UUID pickleable on Python 2.7; since Traits 5.0, these have been pickleable only on Python 3.

This ameliorates (but doesn't really fix) the issues described in #452, and we may still want to look into other solutions to #452. Nevertheless, it seems like a useful minor improvement independent of any other fix applied for #452.

Some remaining awkwardnesses:

  • Range traits aren't currently pickleable on Python 2.7, for similar reasons. This can be fixed in a later PR.
  • An instance of a HasTraits class with a UUID trait in its __instance_traits__ still can't be roundtripped through a pickle operation, because reconstruction runs into the read-only property of the UUID trait. For example, on this branch:
(traits-test-2.7)bash-3.2$ python
Enthought Deployment Manager -- https://www.enthought.com
Python 2.7.13 |Enthought, Inc. (x86_64)| (default, Mar  2 2017, 08:20:50) 
[GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.57)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from traits.api import *
>>> class A(HasTraits): foo = UUID()
... 
>>> a = A()
>>> a.on_trait_change(lambda: None, 'foo')  # force existence of an `__instance_trait__` entry
>>> import pickle
>>> pickle.dumps(a)
"ctraits.traits\n__newobj__\np0\n(c__main__\nA\np1\ntp2\nRp3\n(dp4\nS'__traits_version__'\np5\nS'5.1.0.dev33'\np6\nsS'foo'\np7\nccopy_reg\n_reconstructor\np8\n(cuuid\nUUID\np9\nc__builtin__\nobject\np10\nNtp11\nRp12\n(dp13\nS'int'\np14\nL157809889254046028519129337824636172114L\nsbsS'__instance_traits__'\np15\n(dp16\ng7\ng0\n(ctraits.traits\nCTrait\np17\nI0\ntp18\nRp19\n(I0\nI0\nI5\nNI14\nI-1\nI7\n(cuuid\nuuid4\np20\n(tNtp21\nI0\nNNI4\nNg8\n(ctraits.trait_types\nUUID\np22\ng10\nNtp23\nRp24\n(dp25\nS'default_value'\np26\nNsS'_metadata'\np27\n(dp28\nS'type'\np29\nS'trait'\np30\nssb(dp31\ng29\ng30\nstp32\nbssb."
>>> pickle.loads(pickle.dumps(a))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/mdickinson/.edm/envs/traits-test-2.7/lib/python2.7/pickle.py", line 1388, in loads
    return Unpickler(file).load()
  File "/Users/mdickinson/.edm/envs/traits-test-2.7/lib/python2.7/pickle.py", line 864, in load
    dispatch[key](self)
  File "/Users/mdickinson/.edm/envs/traits-test-2.7/lib/python2.7/pickle.py", line 1223, in load_build
    setstate(state)
  File "/Users/mdickinson/Enthought/ETS/traits/traits/has_traits.py", line 1567, in __setstate__
    self.trait_set(trait_change_notify=trait_change_notify, **state)
  File "/Users/mdickinson/Enthought/ETS/traits/traits/has_traits.py", line 1672, in trait_set
    setattr(self, name, value)
  File "/Users/mdickinson/Enthought/ETS/traits/traits/trait_types.py", line 3704, in validate
    "UUID." % (name, class_of(object))
traits.trait_errors.TraitError: The 'foo' trait of an A instance is a read-only UUID.

@mdickinson
Copy link
Member Author

since Traits 5.0, these have been pickleable only on Python 3.

To be clear, these traits have never been pickleable on Python 2, but #373 caused attempts to pickle these traits to be much more common.

@codecov-io
Copy link

codecov-io commented Mar 18, 2019

Codecov Report

Merging #457 into master will increase coverage by 0.29%.
The diff coverage is 100%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #457      +/-   ##
==========================================
+ Coverage   65.27%   65.57%   +0.29%     
==========================================
  Files          44       44              
  Lines        7053     7055       +2     
  Branches     1410     1410              
==========================================
+ Hits         4604     4626      +22     
+ Misses       2027     2008      -19     
+ Partials      422      421       -1
Impacted Files Coverage Δ
traits/trait_types.py 63.99% <100%> (+0.83%) ⬆️
traits/etsconfig/etsconfig.py 63.58% <0%> (+6.17%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 3883302...870e43b. Read the comment docs.

@jwiggins
Copy link
Member

We made a UUID trait which can be assigned at initialization time for a customer project. It's as easy as adding a small check to the validate method:

if instance.traits_inited():
    raise TraitError('A UUID trait can only be set at object initialization time!')

@mdickinson
Copy link
Member Author

We don't care about Python 2 any more. Closing this PR.

@mdickinson mdickinson closed this Mar 6, 2020
@mdickinson mdickinson deleted the fix/make-instance-traits-pickleable branch March 6, 2020 08:38
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.

3 participants