xref: /openbmc/openbmc/poky/bitbake/lib/bb/COW.py (revision 92b42cb3)
1eb8dc403SDave Cobbley#
2eb8dc403SDave Cobbley# This is a copy on write dictionary and set which abuses classes to try and be nice and fast.
3eb8dc403SDave Cobbley#
4eb8dc403SDave Cobbley# Copyright (C) 2006 Tim Ansell
5eb8dc403SDave Cobbley#
6*92b42cb3SPatrick Williams# SPDX-License-Identifier: GPL-2.0-only
7*92b42cb3SPatrick Williams#
8eb8dc403SDave Cobbley# Please Note:
9eb8dc403SDave Cobbley# Be careful when using mutable types (ie Dict and Lists) - operations involving these are SLOW.
10eb8dc403SDave Cobbley# Assign a file to __warn__ to get warnings about slow operations.
11eb8dc403SDave Cobbley#
12eb8dc403SDave Cobbley
13eb8dc403SDave Cobbley
14eb8dc403SDave Cobbleyimport copy
15c9f7865aSAndrew Geissler
16eb8dc403SDave CobbleyImmutableTypes = (
17eb8dc403SDave Cobbley    bool,
18eb8dc403SDave Cobbley    complex,
19eb8dc403SDave Cobbley    float,
20eb8dc403SDave Cobbley    int,
21eb8dc403SDave Cobbley    tuple,
22eb8dc403SDave Cobbley    frozenset,
23eb8dc403SDave Cobbley    str
24eb8dc403SDave Cobbley)
25eb8dc403SDave Cobbley
26eb8dc403SDave CobbleyMUTABLE = "__mutable__"
27eb8dc403SDave Cobbley
28c9f7865aSAndrew Geissler
29eb8dc403SDave Cobbleyclass COWMeta(type):
30eb8dc403SDave Cobbley    pass
31eb8dc403SDave Cobbley
32c9f7865aSAndrew Geissler
33eb8dc403SDave Cobbleyclass COWDictMeta(COWMeta):
34eb8dc403SDave Cobbley    __warn__ = False
35eb8dc403SDave Cobbley    __hasmutable__ = False
36eb8dc403SDave Cobbley    __marker__ = tuple()
37eb8dc403SDave Cobbley
38eb8dc403SDave Cobbley    def __str__(cls):
39eb8dc403SDave Cobbley        # FIXME: I have magic numbers!
40eb8dc403SDave Cobbley        return "<COWDict Level: %i Current Keys: %i>" % (cls.__count__, len(cls.__dict__) - 3)
41c9f7865aSAndrew Geissler
42eb8dc403SDave Cobbley    __repr__ = __str__
43eb8dc403SDave Cobbley
44eb8dc403SDave Cobbley    def cow(cls):
45eb8dc403SDave Cobbley        class C(cls):
46eb8dc403SDave Cobbley            __count__ = cls.__count__ + 1
47c9f7865aSAndrew Geissler
48eb8dc403SDave Cobbley        return C
49c9f7865aSAndrew Geissler
50eb8dc403SDave Cobbley    copy = cow
51eb8dc403SDave Cobbley    __call__ = cow
52eb8dc403SDave Cobbley
53eb8dc403SDave Cobbley    def __setitem__(cls, key, value):
54eb8dc403SDave Cobbley        if value is not None and not isinstance(value, ImmutableTypes):
55eb8dc403SDave Cobbley            if not isinstance(value, COWMeta):
56eb8dc403SDave Cobbley                cls.__hasmutable__ = True
57eb8dc403SDave Cobbley            key += MUTABLE
58eb8dc403SDave Cobbley        setattr(cls, key, value)
59eb8dc403SDave Cobbley
60eb8dc403SDave Cobbley    def __getmutable__(cls, key, readonly=False):
61eb8dc403SDave Cobbley        nkey = key + MUTABLE
62eb8dc403SDave Cobbley        try:
63eb8dc403SDave Cobbley            return cls.__dict__[nkey]
64eb8dc403SDave Cobbley        except KeyError:
65eb8dc403SDave Cobbley            pass
66eb8dc403SDave Cobbley
67eb8dc403SDave Cobbley        value = getattr(cls, nkey)
68eb8dc403SDave Cobbley        if readonly:
69eb8dc403SDave Cobbley            return value
70eb8dc403SDave Cobbley
71eb8dc403SDave Cobbley        if not cls.__warn__ is False and not isinstance(value, COWMeta):
72eb8dc403SDave Cobbley            print("Warning: Doing a copy because %s is a mutable type." % key, file=cls.__warn__)
73eb8dc403SDave Cobbley        try:
74eb8dc403SDave Cobbley            value = value.copy()
75eb8dc403SDave Cobbley        except AttributeError as e:
76eb8dc403SDave Cobbley            value = copy.copy(value)
77eb8dc403SDave Cobbley        setattr(cls, nkey, value)
78eb8dc403SDave Cobbley        return value
79eb8dc403SDave Cobbley
80eb8dc403SDave Cobbley    __getmarker__ = []
81c9f7865aSAndrew Geissler
82eb8dc403SDave Cobbley    def __getreadonly__(cls, key, default=__getmarker__):
83c9f7865aSAndrew Geissler        """
84eb8dc403SDave Cobbley        Get a value (even if mutable) which you promise not to change.
85eb8dc403SDave Cobbley        """
86eb8dc403SDave Cobbley        return cls.__getitem__(key, default, True)
87eb8dc403SDave Cobbley
88eb8dc403SDave Cobbley    def __getitem__(cls, key, default=__getmarker__, readonly=False):
89eb8dc403SDave Cobbley        try:
90eb8dc403SDave Cobbley            try:
91eb8dc403SDave Cobbley                value = getattr(cls, key)
92eb8dc403SDave Cobbley            except AttributeError:
93eb8dc403SDave Cobbley                value = cls.__getmutable__(key, readonly)
94eb8dc403SDave Cobbley
95eb8dc403SDave Cobbley            # This is for values which have been deleted
96eb8dc403SDave Cobbley            if value is cls.__marker__:
97eb8dc403SDave Cobbley                raise AttributeError("key %s does not exist." % key)
98eb8dc403SDave Cobbley
99eb8dc403SDave Cobbley            return value
100eb8dc403SDave Cobbley        except AttributeError as e:
101eb8dc403SDave Cobbley            if not default is cls.__getmarker__:
102eb8dc403SDave Cobbley                return default
103eb8dc403SDave Cobbley
104eb8dc403SDave Cobbley            raise KeyError(str(e))
105eb8dc403SDave Cobbley
106eb8dc403SDave Cobbley    def __delitem__(cls, key):
107eb8dc403SDave Cobbley        cls.__setitem__(key, cls.__marker__)
108eb8dc403SDave Cobbley
109eb8dc403SDave Cobbley    def __revertitem__(cls, key):
110eb8dc403SDave Cobbley        if key not in cls.__dict__:
111eb8dc403SDave Cobbley            key += MUTABLE
112eb8dc403SDave Cobbley        delattr(cls, key)
113eb8dc403SDave Cobbley
114eb8dc403SDave Cobbley    def __contains__(cls, key):
115eb8dc403SDave Cobbley        return cls.has_key(key)
116eb8dc403SDave Cobbley
117eb8dc403SDave Cobbley    def has_key(cls, key):
118eb8dc403SDave Cobbley        value = cls.__getreadonly__(key, cls.__marker__)
119eb8dc403SDave Cobbley        if value is cls.__marker__:
120eb8dc403SDave Cobbley            return False
121eb8dc403SDave Cobbley        return True
122eb8dc403SDave Cobbley
123eb8dc403SDave Cobbley    def iter(cls, type, readonly=False):
124eb8dc403SDave Cobbley        for key in dir(cls):
125eb8dc403SDave Cobbley            if key.startswith("__"):
126eb8dc403SDave Cobbley                continue
127eb8dc403SDave Cobbley
128eb8dc403SDave Cobbley            if key.endswith(MUTABLE):
129eb8dc403SDave Cobbley                key = key[:-len(MUTABLE)]
130eb8dc403SDave Cobbley
131eb8dc403SDave Cobbley            if type == "keys":
132eb8dc403SDave Cobbley                yield key
133eb8dc403SDave Cobbley
134eb8dc403SDave Cobbley            try:
135eb8dc403SDave Cobbley                if readonly:
136eb8dc403SDave Cobbley                    value = cls.__getreadonly__(key)
137eb8dc403SDave Cobbley                else:
138eb8dc403SDave Cobbley                    value = cls[key]
139eb8dc403SDave Cobbley            except KeyError:
140eb8dc403SDave Cobbley                continue
141eb8dc403SDave Cobbley
142eb8dc403SDave Cobbley            if type == "values":
143eb8dc403SDave Cobbley                yield value
144eb8dc403SDave Cobbley            if type == "items":
145eb8dc403SDave Cobbley                yield (key, value)
1461a4b7ee2SBrad Bishop        return
147eb8dc403SDave Cobbley
148eb8dc403SDave Cobbley    def iterkeys(cls):
149eb8dc403SDave Cobbley        return cls.iter("keys")
150c9f7865aSAndrew Geissler
151eb8dc403SDave Cobbley    def itervalues(cls, readonly=False):
152eb8dc403SDave Cobbley        if not cls.__warn__ is False and cls.__hasmutable__ and readonly is False:
153c9f7865aSAndrew Geissler            print("Warning: If you aren't going to change any of the values call with True.", file=cls.__warn__)
154eb8dc403SDave Cobbley        return cls.iter("values", readonly)
155c9f7865aSAndrew Geissler
156eb8dc403SDave Cobbley    def iteritems(cls, readonly=False):
157eb8dc403SDave Cobbley        if not cls.__warn__ is False and cls.__hasmutable__ and readonly is False:
158c9f7865aSAndrew Geissler            print("Warning: If you aren't going to change any of the values call with True.", file=cls.__warn__)
159eb8dc403SDave Cobbley        return cls.iter("items", readonly)
160eb8dc403SDave Cobbley
161c9f7865aSAndrew Geissler
162eb8dc403SDave Cobbleyclass COWSetMeta(COWDictMeta):
163eb8dc403SDave Cobbley    def __str__(cls):
164eb8dc403SDave Cobbley        # FIXME: I have magic numbers!
165eb8dc403SDave Cobbley        return "<COWSet Level: %i Current Keys: %i>" % (cls.__count__, len(cls.__dict__) - 3)
166c9f7865aSAndrew Geissler
167eb8dc403SDave Cobbley    __repr__ = __str__
168eb8dc403SDave Cobbley
169eb8dc403SDave Cobbley    def cow(cls):
170eb8dc403SDave Cobbley        class C(cls):
171eb8dc403SDave Cobbley            __count__ = cls.__count__ + 1
172c9f7865aSAndrew Geissler
173eb8dc403SDave Cobbley        return C
174eb8dc403SDave Cobbley
175eb8dc403SDave Cobbley    def add(cls, value):
176eb8dc403SDave Cobbley        COWDictMeta.__setitem__(cls, repr(hash(value)), value)
177eb8dc403SDave Cobbley
178eb8dc403SDave Cobbley    def remove(cls, value):
179eb8dc403SDave Cobbley        COWDictMeta.__delitem__(cls, repr(hash(value)))
180eb8dc403SDave Cobbley
181eb8dc403SDave Cobbley    def __in__(cls, value):
182eb8dc403SDave Cobbley        return repr(hash(value)) in COWDictMeta
183eb8dc403SDave Cobbley
184eb8dc403SDave Cobbley    def iterkeys(cls):
185eb8dc403SDave Cobbley        raise TypeError("sets don't have keys")
186eb8dc403SDave Cobbley
187eb8dc403SDave Cobbley    def iteritems(cls):
188eb8dc403SDave Cobbley        raise TypeError("sets don't have 'items'")
189eb8dc403SDave Cobbley
190c9f7865aSAndrew Geissler
191eb8dc403SDave Cobbley# These are the actual classes you use!
192c9f7865aSAndrew Geisslerclass COWDictBase(metaclass=COWDictMeta):
193eb8dc403SDave Cobbley    __count__ = 0
194eb8dc403SDave Cobbley
195c9f7865aSAndrew Geissler
196c9f7865aSAndrew Geisslerclass COWSetBase(metaclass=COWSetMeta):
197eb8dc403SDave Cobbley    __count__ = 0
198