aboutsummaryrefslogtreecommitdiffstats
path: root/lib/bb/COW.py
diff options
context:
space:
mode:
authorTim Ansell <mithro@mithis.com>2006-04-24 09:54:32 +0000
committerTim Ansell <mithro@mithis.com>2006-04-24 09:54:32 +0000
commit629e9df5c7384c5470e37470b0bfe7657d61a8d3 (patch)
treefe591a31aa886cbc3884416a96461f90b6245c8b /lib/bb/COW.py
parentb0d769ce515d866eea92011741b8dd5423bd5360 (diff)
downloadbitbake-629e9df5c7384c5470e37470b0bfe7657d61a8d3.tar.gz
Delete now works. Other cleanup and fixes.
Diffstat (limited to 'lib/bb/COW.py')
-rw-r--r--lib/bb/COW.py243
1 files changed, 198 insertions, 45 deletions
diff --git a/lib/bb/COW.py b/lib/bb/COW.py
index d34dd5f24..826d435f9 100644
--- a/lib/bb/COW.py
+++ b/lib/bb/COW.py
@@ -1,9 +1,11 @@
# ex:ts=4:sw=4:sts=4:et
# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
"""
-This is a copy on write dictionary which abuses classes to be nice and fast.
+This is a copy on write dictionary and set which abuses classes to try and be nice and fast.
-Please Note: Be careful when using mutable types (ie Dict and Lists). The copy on write stuff only kicks in on Assignment.
+Please Note:
+ Be careful when using mutable types (ie Dict and Lists) - operations involving these are SLOW.
+ Assign a file to __warn__ to get warnings about slow operations.
"""
from inspect import getmro
@@ -21,57 +23,95 @@ types.ImmutableTypes = tuple([ \
sets.ImmutableSet] + \
list(types.StringTypes))
-MUTABLE = "_mutable__"
+MUTABLE = "__mutable__"
+
+class COWMeta(type):
+ pass
-class COWDictMeta(type):
+class COWDictMeta(COWMeta):
+ __warn__ = False
+ __hasmutable__ = False
+ __marker__ = tuple()
+
def __str__(cls):
- return "<COWDict Level: %i Current Keys: %i>" % (cls.__count__, len(cls.__dict__))
+ # FIXME: I have magic numbers!
+ return "<COWDict Level: %i Current Keys: %i>" % (cls.__count__, len(cls.__dict__) - 3)
__repr__ = __str__
def cow(cls):
class C(cls):
__count__ = cls.__count__ + 1
return C
+ copy = cow
+ __call__ = cow
def __setitem__(cls, key, value):
if not isinstance(value, types.ImmutableTypes):
+ if not isinstance(value, COWMeta):
+ cls.__hasmutable__ = True
key += MUTABLE
setattr(cls, key, value)
- def __getmutable__(cls, key):
- """
- This gets called when you do a "o.b" and b doesn't exist on o.
-
- IE When the type is mutable.
- """
+ def __getmutable__(cls, key, readonly=False):
nkey = key + MUTABLE
- # Do we already have a copy of this on us?
- if nkey in cls.__dict__:
+ try:
return cls.__dict__[nkey]
+ except KeyError:
+ pass
- r = getattr(cls, nkey)
+ value = getattr(cls, nkey)
+ if readonly:
+ return value
+
+ if not cls.__warn__ is False and not isinstance(value, COWMeta):
+ print >> cls.__warn__, "Warning: Doing a copy because %s is a mutable type." % key
try:
- r = r.copy()
- except NameError, e:
- r = copy.copy(r)
- setattr(cls, nkey, r)
- return r
+ value = value.copy()
+ except AttributeError, e:
+ value = copy.copy(value)
+ setattr(cls, nkey, value)
+ return value
- def __getitem__(cls, key):
+ __getmarker__ = []
+ def __getreadonly__(cls, key, default=__getmarker__):
+ """\
+ Get a value (even if mutable) which you promise not to change.
+ """
+ return cls.__getitem__(key, default, True)
+
+ def __getitem__(cls, key, default=__getmarker__, readonly=False):
try:
try:
- return getattr(cls, key)
+ value = getattr(cls, key)
except AttributeError:
- # Degrade to slow mode if type is not immutable,
- # If the type is also a COW this will be cheap anyway
- return cls.__getmutable__(key)
+ value = cls.__getmutable__(key, readonly)
+
+ # This is for values which have been deleted
+ if value is cls.__marker__:
+ raise AttributeError("key %s does not exist." % key)
+
+ return value
except AttributeError, e:
- raise KeyError(e)
+ if not default is cls.__getmarker__:
+ return default
+
+ raise KeyError(str(e))
+
+ def __delitem__(cls, key):
+ cls.__setitem__(key, cls.__marker__)
+
+ def __revertitem__(cls, key):
+ if not cls.__dict__.has_key(key):
+ key += MUTABLE
+ delattr(cls, key)
def has_key(cls, key):
- return (hasattr(cls, key) or hasattr(cls, key + MUTABLE))
+ value = cls.__getreadonly__(key, cls.__marker__)
+ if value is cls.__marker__:
+ return False
+ return True
- def iter(cls, type):
+ def iter(cls, type, readonly=False):
for key in dir(cls):
if key.startswith("__"):
continue
@@ -81,23 +121,36 @@ class COWDictMeta(type):
if type == "keys":
yield key
+
+ try:
+ if readonly:
+ value = cls.__getreadonly__(key)
+ else:
+ value = cls[key]
+ except KeyError:
+ continue
+
if type == "values":
- yield cls[key]
+ yield value
if type == "items":
- yield (key, cls[key])
+ yield (key, value)
raise StopIteration()
def iterkeys(cls):
return cls.iter("keys")
- def itervalues(cls):
- return cls.iter("values")
- def iteritems(cls):
- return cls.iter("items")
- copy = cow
+ def itervalues(cls, readonly=False):
+ if not cls.__warn__ is False and cls.__hasmutable__ and readonly is False:
+ print >> cls.__warn__, "Warning: If you arn't going to change any of the values call with True."
+ return cls.iter("values", readonly)
+ def iteritems(cls, readonly=False):
+ if not cls.__warn__ is False and cls.__hasmutable__ and readonly is False:
+ print >> cls.__warn__, "Warning: If you arn't going to change any of the values call with True."
+ return cls.iter("items", readonly)
class COWSetMeta(COWDictMeta):
def __str__(cls):
- return "<COWSet Level: %i Current Keys: %i>" % (cls.__count__, len(cls.__dict__))
+ # FIXME: I have magic numbers!
+ return "<COWSet Level: %i Current Keys: %i>" % (cls.__count__, len(cls.__dict__) -3)
__repr__ = __str__
def cow(cls):
@@ -105,31 +158,51 @@ class COWSetMeta(COWDictMeta):
__count__ = cls.__count__ + 1
return C
- def add(cls, key, value):
- cls.__setitem__(hash(key), value)
+ def add(cls, value):
+ COWDictMeta.__setitem__(cls, repr(hash(value)), value)
+
+ def remove(cls, value):
+ COWDictMeta.__delitem__(cls, repr(hash(value)))
+
+ def __in__(cls, value):
+ return COWDictMeta.has_key(repr(hash(value)))
+
+ def iterkeys(cls):
+ raise TypeError("sets don't have keys")
+
+ def iteritems(cls):
+ raise TypeError("sets don't have 'items'")
+# These are the actual classes you use!
class COWDictBase(object):
__metaclass__ = COWDictMeta
__count__ = 0
+class COWSetBase(object):
+ __metaclass__ = COWSetMeta
+ __count__ = 0
if __name__ == "__main__":
- a = COWDictBase.copy()
- print a
+ import sys
+ COWDictBase.__warn__ = sys.stderr
+ a = COWDictBase()
+ print "a", a
+
a['a'] = 'a'
+ a['b'] = 'b'
a['dict'] = {}
- print "ha"
- hasattr(a, "a")
- print "ha"
-
b = a.copy()
- print b
- b['b'] = 'b'
+ print "b", b
+ b['c'] = 'b'
+
+ print
+ print "a", a
for x in a.iteritems():
print x
print "--"
+ print "b", b
for x in b.iteritems():
print x
print
@@ -137,9 +210,11 @@ if __name__ == "__main__":
b['dict']['a'] = 'b'
b['a'] = 'c'
+ print "a", a
for x in a.iteritems():
print x
print "--"
+ print "b", b
for x in b.iteritems():
print x
print
@@ -149,4 +224,82 @@ if __name__ == "__main__":
except KeyError, e:
print "Okay!"
+ a['set'] = COWSetBase()
+ a['set'].add("o1")
+ a['set'].add("o1")
+ a['set'].add("o2")
+ print "a", a
+ for x in a['set'].itervalues():
+ print x
+ print "--"
+ print "b", b
+ for x in b['set'].itervalues():
+ print x
+ print
+
+ b['set'].add('o3')
+
+ print "a", a
+ for x in a['set'].itervalues():
+ print x
+ print "--"
+ print "b", b
+ for x in b['set'].itervalues():
+ print x
+ print
+
+ a['set2'] = set()
+ a['set2'].add("o1")
+ a['set2'].add("o1")
+ a['set2'].add("o2")
+
+ print "a", a
+ for x in a.iteritems():
+ print x
+ print "--"
+ print "b", b
+ for x in b.iteritems(readonly=True):
+ print x
+ print
+
+ del b['b']
+ try:
+ print b['b']
+ except KeyError:
+ print "Yay! deleted key raises error"
+
+ if b.has_key('b'):
+ print "Boo!"
+ else:
+ print "Yay - has_key with delete works!"
+
+ print "a", a
+ for x in a.iteritems():
+ print x
+ print "--"
+ print "b", b
+ for x in b.iteritems(readonly=True):
+ print x
+ print
+
+ b.__revertitem__('b')
+
+ print "a", a
+ for x in a.iteritems():
+ print x
+ print "--"
+ print "b", b
+ for x in b.iteritems(readonly=True):
+ print x
+ print
+
+ b.__revertitem__('dict')
+ print "a", a
+ for x in a.iteritems():
+ print x
+ print "--"
+ print "b", b
+ for x in b.iteritems(readonly=True):
+ print x
+ print