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