From 749f4f67444c7be7fe7aac0189776fd540975e09 Mon Sep 17 00:00:00 2001 From: Christopher Larson Date: Mon, 10 Sep 2012 22:23:19 +0000 Subject: bitbake: compat, event: use OrderedDict from py2.7 for the event handlers This ensures that our event handlers get run in registration order, making the behavior more deterministic. I pulled in the python2.7 OrderedDict to avoid essentially reimplementing a version of it ourselves, figuring we can drop it when we bump our required python version next. (Bitbake rev: 44aa0b0537d3fbd1272015e7677948f84d8c0607) Signed-off-by: Christopher Larson Signed-off-by: Richard Purdie --- bitbake/lib/bb/compat.py | 213 +++++++++++++++++++++++++++++++++++++++++++++++ bitbake/lib/bb/event.py | 3 +- 2 files changed, 215 insertions(+), 1 deletion(-) diff --git a/bitbake/lib/bb/compat.py b/bitbake/lib/bb/compat.py index c6978fccc5..1466da2379 100644 --- a/bitbake/lib/bb/compat.py +++ b/bitbake/lib/bb/compat.py @@ -1,5 +1,11 @@ """Code pulled from future python versions, here for compatibility""" +from collections import MutableMapping, KeysView, ValuesView, ItemsView +try: + from thread import get_ident as _get_ident +except ImportError: + from dummy_thread import get_ident as _get_ident + def total_ordering(cls): """Class decorator that fills in missing ordering methods""" convert = { @@ -26,3 +32,210 @@ def total_ordering(cls): opfunc.__doc__ = getattr(int, opname).__doc__ setattr(cls, opname, opfunc) return cls + +class OrderedDict(dict): + 'Dictionary that remembers insertion order' + # An inherited dict maps keys to values. + # The inherited dict provides __getitem__, __len__, __contains__, and get. + # The remaining methods are order-aware. + # Big-O running times for all methods are the same as regular dictionaries. + + # The internal self.__map dict maps keys to links in a doubly linked list. + # The circular doubly linked list starts and ends with a sentinel element. + # The sentinel element never gets deleted (this simplifies the algorithm). + # Each link is stored as a list of length three: [PREV, NEXT, KEY]. + + def __init__(self, *args, **kwds): + '''Initialize an ordered dictionary. The signature is the same as + regular dictionaries, but keyword arguments are not recommended because + their insertion order is arbitrary. + + ''' + if len(args) > 1: + raise TypeError('expected at most 1 arguments, got %d' % len(args)) + try: + self.__root + except AttributeError: + self.__root = root = [] # sentinel node + root[:] = [root, root, None] + self.__map = {} + self.__update(*args, **kwds) + + def __setitem__(self, key, value, PREV=0, NEXT=1, dict_setitem=dict.__setitem__): + 'od.__setitem__(i, y) <==> od[i]=y' + # Setting a new item creates a new link at the end of the linked list, + # and the inherited dictionary is updated with the new key/value pair. + if key not in self: + root = self.__root + last = root[PREV] + last[NEXT] = root[PREV] = self.__map[key] = [last, root, key] + dict_setitem(self, key, value) + + def __delitem__(self, key, PREV=0, NEXT=1, dict_delitem=dict.__delitem__): + 'od.__delitem__(y) <==> del od[y]' + # Deleting an existing item uses self.__map to find the link which gets + # removed by updating the links in the predecessor and successor nodes. + dict_delitem(self, key) + link_prev, link_next, key = self.__map.pop(key) + link_prev[NEXT] = link_next + link_next[PREV] = link_prev + + def __iter__(self): + 'od.__iter__() <==> iter(od)' + # Traverse the linked list in order. + NEXT, KEY = 1, 2 + root = self.__root + curr = root[NEXT] + while curr is not root: + yield curr[KEY] + curr = curr[NEXT] + + def __reversed__(self): + 'od.__reversed__() <==> reversed(od)' + # Traverse the linked list in reverse order. + PREV, KEY = 0, 2 + root = self.__root + curr = root[PREV] + while curr is not root: + yield curr[KEY] + curr = curr[PREV] + + def clear(self): + 'od.clear() -> None. Remove all items from od.' + for node in self.__map.itervalues(): + del node[:] + root = self.__root + root[:] = [root, root, None] + self.__map.clear() + dict.clear(self) + + # -- the following methods do not depend on the internal structure -- + + def keys(self): + 'od.keys() -> list of keys in od' + return list(self) + + def values(self): + 'od.values() -> list of values in od' + return [self[key] for key in self] + + def items(self): + 'od.items() -> list of (key, value) pairs in od' + return [(key, self[key]) for key in self] + + def iterkeys(self): + 'od.iterkeys() -> an iterator over the keys in od' + return iter(self) + + def itervalues(self): + 'od.itervalues -> an iterator over the values in od' + for k in self: + yield self[k] + + def iteritems(self): + 'od.iteritems -> an iterator over the (key, value) pairs in od' + for k in self: + yield (k, self[k]) + + update = MutableMapping.update + + __update = update # let subclasses override update without breaking __init__ + + __marker = object() + + def pop(self, key, default=__marker): + '''od.pop(k[,d]) -> v, remove specified key and return the corresponding + value. If key is not found, d is returned if given, otherwise KeyError + is raised. + + ''' + if key in self: + result = self[key] + del self[key] + return result + if default is self.__marker: + raise KeyError(key) + return default + + def setdefault(self, key, default=None): + 'od.setdefault(k[,d]) -> od.get(k,d), also set od[k]=d if k not in od' + if key in self: + return self[key] + self[key] = default + return default + + def popitem(self, last=True): + '''od.popitem() -> (k, v), return and remove a (key, value) pair. + Pairs are returned in LIFO order if last is true or FIFO order if false. + + ''' + if not self: + raise KeyError('dictionary is empty') + key = next(reversed(self) if last else iter(self)) + value = self.pop(key) + return key, value + + def __repr__(self, _repr_running={}): + 'od.__repr__() <==> repr(od)' + call_key = id(self), _get_ident() + if call_key in _repr_running: + return '...' + _repr_running[call_key] = 1 + try: + if not self: + return '%s()' % (self.__class__.__name__,) + return '%s(%r)' % (self.__class__.__name__, self.items()) + finally: + del _repr_running[call_key] + + def __reduce__(self): + 'Return state information for pickling' + items = [[k, self[k]] for k in self] + inst_dict = vars(self).copy() + for k in vars(OrderedDict()): + inst_dict.pop(k, None) + if inst_dict: + return (self.__class__, (items,), inst_dict) + return self.__class__, (items,) + + def copy(self): + 'od.copy() -> a shallow copy of od' + return self.__class__(self) + + @classmethod + def fromkeys(cls, iterable, value=None): + '''OD.fromkeys(S[, v]) -> New ordered dictionary with keys from S. + If not specified, the value defaults to None. + + ''' + self = cls() + for key in iterable: + self[key] = value + return self + + def __eq__(self, other): + '''od.__eq__(y) <==> od==y. Comparison to another OD is order-sensitive + while comparison to a regular mapping is order-insensitive. + + ''' + if isinstance(other, OrderedDict): + return len(self)==len(other) and self.items() == other.items() + return dict.__eq__(self, other) + + def __ne__(self, other): + 'od.__ne__(y) <==> od!=y' + return not self == other + + # -- the following methods support python 3.x style dictionary views -- + + def viewkeys(self): + "od.viewkeys() -> a set-like object providing a view on od's keys" + return KeysView(self) + + def viewvalues(self): + "od.viewvalues() -> an object providing a view on od's values" + return ValuesView(self) + + def viewitems(self): + "od.viewitems() -> a set-like object providing a view on od's items" + return ItemsView(self) diff --git a/bitbake/lib/bb/event.py b/bitbake/lib/bb/event.py index ab62d4d055..7ee28fcfcb 100644 --- a/bitbake/lib/bb/event.py +++ b/bitbake/lib/bb/event.py @@ -32,6 +32,7 @@ import logging import atexit import traceback import bb.utils +import bb.compat # This is the pid for which we should generate the event. This is set when # the runqueue forks off. @@ -53,7 +54,7 @@ Registered = 10 AlreadyRegistered = 14 # Internal -_handlers = {} +_handlers = bb.compat.OrderedDict() _ui_handlers = {} _ui_handler_seq = 0 -- cgit v1.2.3-54-g00ecf