diff --git a/apptools/persistence/spickle.py b/apptools/persistence/spickle.py deleted file mode 100644 index 614bd2483..000000000 --- a/apptools/persistence/spickle.py +++ /dev/null @@ -1,331 +0,0 @@ -"""A special unpickler that gives you a state object and a special -pickler that lets you re-pickle that state. - -The nice thing about creating state objects is that it does not import -any modules and does not create any instances. Instead of instances -it creates `State` instances which have the same attributes as the -real object. With this you can load a pickle (without even having the -modules on the machine), modify it and re-pickle it back. - -NOTE: This module is not likely to work for very complex pickles but - it should work for most common cases. - -""" -# Author: Prabhu Ramachandran -# Copyright (c) 2006-2015, Prabhu Ramachandran -# License: BSD Style. - -import sys - -if sys.version_info[0] > 2: - raise ImportError("This module does not work with Python 3.x") - -import warnings -import pickle -import struct -from pickle import ( - Pickler, - Unpickler, - dumps, - BUILD, - NEWOBJ, - REDUCE, - MARK, - OBJ, - INST, - BUILD, - PicklingError, - GLOBAL, - EXT1, - EXT2, - EXT4, - _extension_registry, - _keep_alive, -) - -from io import BytesIO - - -###################################################################### -# `State` class -###################################################################### -class State(dict): - """Used to encapsulate the state of an instance in a very - convenient form. The '__METADATA__' attribute/key is a dictionary - that has class specific details like the class name, module name - etc. - """ - - def __init__(self, **kw): - dict.__init__(self, **kw) - self.__dict__ = self - - -###################################################################### -# `StatePickler` class -###################################################################### -class StatePickler(Pickler): - """Pickles a `State` object back as a regular pickle that may be - unpickled. - """ - - def __init__(self, file, protocol, bin=None): - Pickler.__init__(self, file, protocol) - self.bin = bin - - def save(self, obj): - # Check the memo - x = self.memo.get(id(obj)) - if x: - self.write(self.get(x[0])) - return - - if isinstance(obj, State): - md = obj.__METADATA__ - typ = md["type"] - if typ == "instance": - self._state_instance(obj) - elif typ in ["newobj", "reduce"]: - self._state_reduce(obj) - elif typ in ["class"]: - self._save_global(obj) - else: - Pickler.save(self, obj) - - def _state_instance(self, obj): - md = obj.__METADATA__ - cls = md.get("class") - cls_md = cls.__METADATA__ - - memo = self.memo - write = self.write - save = self.save - - args = md.get("initargs") - if len(args) > 0: - _keep_alive(args, memo) - - write(MARK) - - if self.bin: - save(cls) - for arg in args: - save(arg) - write(OBJ) - else: - for arg in args: - save(arg) - write( - INST + cls_md.get("module") + "\n" + cls_md.get("name") + "\n" - ) - - self.memoize(obj) - - stuff = dict(obj.__dict__) - stuff.pop("__METADATA__") - - if "__setstate_data__" in stuff: - data = stuff.pop("__setstate_data__") - _keep_alive(data, memo) - save(data) - else: - save(stuff) - write(BUILD) - - def _state_reduce(self, obj): - # FIXME: this code is not as complete as pickle's reduce - # handling code and is likely to not work in all cases. - - md = obj.__METADATA__ - func = md.get("class") - func_md = func.__METADATA__ - args = md.get("initargs") - state = dict(obj.__dict__) - state.pop("__METADATA__") - - # This API is called by some subclasses - - # Assert that args is a tuple or None - if not isinstance(args, tuple): - if args is None: - # A hack for Jim Fulton's ExtensionClass, now deprecated. - # See load_reduce() - warnings.warn( - "__basicnew__ special case is deprecated", - DeprecationWarning, - ) - else: - raise PicklingError("args from reduce() should be a tuple") - - save = self.save - write = self.write - - # Protocol 2 special case: if func's name is __newobj__, use NEWOBJ - if self.proto >= 2 and func_md.get("name", "") == "__newobj__": - # FIXME: this is unlikely to work. - cls = args[0] - if not hasattr(cls, "__new__"): - raise PicklingError( - "args[0] from __newobj__ args has no __new__" - ) - if obj is not None and cls is not obj.__class__: - raise PicklingError( - "args[0] from __newobj__ args has the wrong class" - ) - args = args[1:] - save(cls) - save(args) - write(NEWOBJ) - else: - save(func) - save(args) - write(REDUCE) - - if obj is not None: - self.memoize(obj) - - if state is not None: - if "__setstate_data__" in state: - data = state.pop("__setstate_data__") - save(data) - else: - save(state) - write(BUILD) - - def _save_global(self, obj, name=None, pack=struct.pack): - write = self.write - memo = self.memo - - md = obj.__METADATA__ - - if name is None: - name = md.get("name") - - module = md.get("module") - - if self.proto >= 2: - code = _extension_registry.get((module, name)) - if code: - assert code > 0 - if code <= 0xFF: - write(EXT1 + chr(code)) - elif code <= 0xFFFF: - write("%c%c%c" % (EXT2, code & 0xFF, code >> 8)) - else: - write(EXT4 + pack(" -# Copyright (c) 2007, Enthought, Inc. -# License: BSD Style. - - -import unittest -import numpy -from pickle import dumps - -try: - from apptools.persistence import spickle -except ImportError: - raise unittest.SkipTest("spickle is not supported with Python3") - -from traits.api import HasTraits, Float, Int - - -class A: - def __init__(self): - self.a = 100 - self.array = numpy.linspace(0, 1, 5) - - -class B(HasTraits): - i = Int(10) - f = Float(1.0) - - -class Foo(object): - def __init__(self, a=1): - self.a = A() - self.a.b = 200 - self.ref = self.a - self.b = B() - self.b.trait_set(i=20, f=2.0) - - -class TestStatePickler(unittest.TestCase): - def _test_object(self, x): - assert x.a.a == 100 - assert numpy.all(x.a.array == numpy.linspace(0, 1, 5)) - assert x.a.b == 200 - assert x.a == x.ref - assert x.b.i == 20 - assert x.b.f == 2.0 - - def test_dump_state(self): - "Test if we are able to dump the state to a string." - f = Foo() - str = dumps(f) - st = spickle.get_state(f) - str1 = spickle.dumps_state(st) - self.assertEqual(str, str1) - st = spickle.loads_state(str) - self.assertEqual(str, spickle.dumps_state(st)) - - def test_state2object(self): - "Test if we can convert a state to an object." - f = Foo() - str = dumps(f) - st = spickle.get_state(f) - g = spickle.state2object(st) - self._test_object(g) diff --git a/docs/releases/upcoming/220.removal.rst b/docs/releases/upcoming/220.removal.rst new file mode 100644 index 000000000..3ff4e85aa --- /dev/null +++ b/docs/releases/upcoming/220.removal.rst @@ -0,0 +1 @@ +Remove ``apptools.persistence.spickle.py`` submodule (#220) \ No newline at end of file