WARNING: THIS SITE IS A MIRROR OF GITHUB.COM / IT CANNOT LOGIN OR REGISTER ACCOUNTS / THE CONTENTS ARE PROVIDED AS-IS / THIS SITE ASSUMES NO RESPONSIBILITY FOR ANY DISPLAYED CONTENT OR LINKS / IF YOU FOUND SOMETHING MAY NOT GOOD FOR EVERYONE, CONTACT ADMIN AT ilovescratch@foxmail.com
Skip to content

Copy On Write for the object state #47

@CastixGitHub

Description

@CastixGitHub

answer to #45 (comment)

I implemented it using a high level approach, should be good enough to be used with pymongo dictionaries, but I didn't think about supporting other kind of objects (that would lead again to deepcopy, unless we are able to drop the abstractions and go low level, but I didn't find anything about copy on write on the python ecosystem (this is an interesting read but unrelated) so i don't know how to proceed in that direction, i'm not a kernel developer)

Proof Of Concept

from collections.abc import Mapping, ValuesView


class CopyOnWrite(Mapping):
    def __init__(self, data):
        self.__dict__['_diff'] = {}
        self.__dict__['_data'] = {}
        for k, v in data.items():
            self._data[k] = v if not isinstance(v, dict) else self.__class__(v)

    def __getitem__(self, key):
        if key in self._diff:
            return self._diff[key]
        return self._data[key]

    def __setitem__(self, key, value):
        if not isinstance(value, dict):
            self._diff[key] = value
        else:
            self._diff[key] = self.__class__(value)

    __setattr__ = __setitem__
    __getattr__ = __getitem__

    @property
    def original(self):
        res = {}
        for k, v in self._data.items():
            if isinstance(v, self.__class__):
                res[k] = v.original
            else:
                res[k] = v
        return res

    def __len__(self):
        return len(self._data | self._diff)

    def __iter__(self):
        return iter(self._data | self._diff)

    def values(self):
        # TODO: unsure about this, docs says a view should reflect the changes
        # TODO: does this work at deeper levels?
        values = self._data | self._diff
        for k, v in values.items():
            if isinstance(v, self.__class__):
                values[k] = v._data | v._diff
        return ValuesView(values)
        


if __name__ == '__main__':
    original = {
        'a': 'A',
        'd': {'z': 'Z'},
        'l': [1, 2, 3],
    }
    cow = CopyOnWrite(original)

    assert cow.original == original, cow.original
    
    cow.a = 'AA'
    assert cow.original == original, cow.original
    assert cow.a == 'AA'
    assert cow['a'] == 'AA'

    cow.d.x = 'X'
    cow.d.z = 'ZZZ'
    assert cow.original == original, cow.original
    assert cow.d.x == 'X'
    assert cow.d.z == 'ZZZ'

    cow.l.append(4)
    assert cow.original == original, cow.original
    assert cow.l[-1] == 4

    cow.n = 'new'
    assert cow.original == original, cow.original
    
    assert len(cow) == 4, len(cow)

    for i, n in enumerate(cow):
        assert n == ('a', 'd', 'l', 'n')[i]

    assert list(cow.keys()) == list(original.keys()) + ['n']

    assert list(cow.values()) == ['AA', {'x': 'X', 'z': 'ZZZ'}, [1, 2, 3, 4], 'new']

    assert dict(cow.items()) == dict(zip(list(original.keys()) + ['n'],
                                         ['AA', {'x': 'X', 'z': 'ZZZ'}, [1, 2, 3, 4], 'new']))

How to apply to the current Object/ObjectState/Tracker

I need to check.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions