import pytest import preserve class PlainClass: def __init__(self): self.attr_int = 734 self.attr_string = "I'm a test string" self.attr_float = 42.085 self.attr_dict = {"key1": 1, "key2": "val2"} self.attr_list = ["item1", 2, 3, 4] self.attr_bool = True @preserve.preservable class PreservableClass(PlainClass): pass @pytest.fixture def primitive_data(): return {'key1': ["item1", {"d2k1": "val1", "d2k2": 2}, {"d3k1": "val1", "d3k2": True}, "test str"]} def obj_attrs_and_type_equal(obj1, obj2): # Checks that both the __dict__ on each object and the __class__ on each object # are equal return (vars(type(obj1)) == vars(type(obj2))) and (vars(obj1) == vars(obj2)) def test_primitive_data(primitive_data): # preserve/restore shouldn't modify something that's already all primitives preserved_data = preserve.preserve(primitive_data) assert preserved_data == primitive_data assert (preserve.restore(preserved_data)) == primitive_data def test_data_with_preservable(primitive_data): # Preservable class within a data structure primitive_data["preservable"] = PreservableClass() restored_data = preserve.restore(preserve.preserve(primitive_data)) assert restored_data['key1'] == primitive_data['key1'] assert restored_data["preservable"] != primitive_data["preservable"] assert obj_attrs_and_type_equal(restored_data["preservable"], primitive_data["preservable"]) def test_plain_class(): # plain class is not preservable with pytest.raises(Exception, match="is not preservable"): preserve.preserve(PlainClass()) def test_preservable_class(): # Preservable class should restore to a new instance with same content obj = PreservableClass() restored_obj = preserve.restore(preserve.preserve(obj)) assert restored_obj != obj assert obj_attrs_and_type_equal(restored_obj, obj) def test_attr_plain_class(): # Class with non-preservable class as an attribute should fail obj = PreservableClass() obj.attr_obj = PlainClass() with pytest.raises(Exception, match="is not preservable"): preserve.preserve(obj) def test_class_key(): # Should be able to change the default class key before decorators # and have preserve/restore work old_class_key = preserve.class_key preserve.class_key = "A different key" @preserve.preservable class TestClass(PlainClass): pass obj = TestClass() restored_obj = preserve.restore(preserve.preserve(obj)) assert restored_obj != obj assert obj_attrs_and_type_equal(restored_obj, obj) preserve.class_key = old_class_key def test_class_key_in_data(): # Can't use the class key as dict key being preserved with pytest.raises(Exception, match="reserved for internal use"): preserve.preserve({preserve.class_key: 1}) def test_unrestorable(primitive_data): # Shouldn't be able to restore from data containing non-primitive types preserved_data = preserve.preserve(primitive_data) preserved_data["unrestorable"] = (1, 2, 3) with pytest.raises(Exception, match="is not restorable"): preserve.restore(preserved_data) preserved_data["unrestorable"] = PlainClass() with pytest.raises(Exception, match="is not restorable"): preserve.restore(preserved_data) def test_str_dict_keys(primitive_data): # Non-string dict keys aren't restorable or preservable preserved_data = preserve.preserve(primitive_data) primitive_data[2] = "has a non-string key" with pytest.raises(Exception, match="Non-string dictionary keys are not preservable"): preserve.preserve(primitive_data) preserved_data[2] = "has a non-string key" with pytest.raises(Exception, match="Non-string dictionary keys are not restorable"): preserve.restore(preserved_data)