Browse Source

Update TwoWayOrderedDict to latest version

doc-issue-template
MrS0m30n3 9 years ago
parent
commit
5ab0035c09
1 changed files with 120 additions and 142 deletions
  1. 262
      youtube_dl_gui/utils.py

262
youtube_dl_gui/utils.py

@ -3,7 +3,7 @@
"""Youtubedlg module that contains util functions. """Youtubedlg module that contains util functions.
Attributes: Attributes:
RANDOM_OBJECT (object): Object that it's used as a default parameter.
_RANDOM_OBJECT (object): Object that it's used as a default parameter.
YOUTUBEDL_BIN (string): Youtube-dl binary filename. YOUTUBEDL_BIN (string): Youtube-dl binary filename.
@ -15,7 +15,7 @@ import sys
import subprocess import subprocess
RANDOM_OBJECT = object()
_RANDOM_OBJECT = object()
YOUTUBEDL_BIN = 'youtube-dl' YOUTUBEDL_BIN = 'youtube-dl'
@ -163,7 +163,7 @@ def get_icon_file():
return None return None
class TwoWayOrderedDict(object):
class TwoWayOrderedDict(dict):
"""Custom data structure which implements a two way ordrered dictionary. """Custom data structure which implements a two way ordrered dictionary.
@ -171,7 +171,7 @@ class TwoWayOrderedDict(object):
key:value relationship but you can also get the value:key relationship. key:value relationship but you can also get the value:key relationship.
It also remembers the order in which the items were inserted and supports It also remembers the order in which the items were inserted and supports
almost all the features of the build-in dict. almost all the features of the build-in dict.
Note: Note:
Ways to create a new dictionary. Ways to create a new dictionary.
@ -188,200 +188,178 @@ class TwoWayOrderedDict(object):
>>> d[1] >>> d[1]
'a' 'a'
>>> print d >>> print d
{'a': 1, 'b': 2}
TwoWayOrderedDict([('a', 1), ('b', 2)])
""" """
def __init__(self, *args, **kwargs):
self._items = list()
self._load_into_dict(args, kwargs)
_PREV = 0
_KEY = 1
_NEXT = 2
def __getitem__(self, key):
try:
item = self._find_item(key)
return self._get_value(key, item)
except KeyError as error:
raise error
def __init__(self, *args, **kwargs):
self._items = item = []
self._items += [item, None, item] # Double linked list [prev, key, next]
self._items_map = {} # Map link list items into keys to speed up lookup
self._load(args, kwargs)
def __setitem__(self, key, value): def __setitem__(self, key, value):
index = 0
while index < len(self._items):
item = self._items[index]
if key in self:
# If self[key] == key for example {'b': 'b'} and we
# do d['b'] = 2 then we dont want to remove the 'b'
# from our linked list because we will lose the order
if self[key] in self._items_map and key != self[key]:
self._remove_mapped_key(self[key])
dict.__delitem__(self, self[key])
if value in self:
# If value == key we dont have to remove the
# value from the items_map because the value is
# the key and we want to keep the key in our
# linked list in order to keep the order.
if value in self._items_map and key != value:
self._remove_mapped_key(value)
if self[value] in self._items_map:
self._remove_mapped_key(self[value])
# Check if self[value] is in the dict
# for cases like {'a': 'a'} where we
# have only one copy instead of {'a': 1, 1: 'a'}
if self[value] in self:
dict.__delitem__(self, self[value])
if key not in self._items_map:
last = self._items[self._PREV] # self._items prev always points to the last item
last[self._NEXT] = self._items[self._PREV] = self._items_map[key] = [last, key, self._items]
dict.__setitem__(self, key, value)
dict.__setitem__(self, value, key)
if (key == item[0] or key == item[1] or
value == item[0] or value == item[1]):
self._items.remove(item)
else:
index += 1
def __delitem__(self, key):
if self[key] in self._items_map:
self._remove_mapped_key(self[key])
self._items.append((key, value))
if key in self._items_map:
self._remove_mapped_key(key)
def __delitem__(self, key):
try:
item = self._find_item(key)
dict.__delitem__(self, self[key])
self._items.remove(item)
except KeyError as error:
raise error
# Check if key is in the dict
# for cases like {'a': 'a'} where we
# have only one copy instead of {'a': 1, 1: 'a'}
if key in self:
dict.__delitem__(self, key)
def __len__(self): def __len__(self):
return len(self._items)
return len(self._items_map)
def __iter__(self): def __iter__(self):
for item in self._items:
yield item[0]
def __contains__(self, item):
"""Return True if the item matches either a
dictionary key or value else False. """
try:
self._find_item(item)
except KeyError:
return False
curr = self._items[self._NEXT]
while curr is not self._items:
yield curr[self._KEY]
curr = curr[self._NEXT]
return True
def __reversed__(self):
curr = self._items[self._PREV]
while curr is not self._items:
yield curr[self._KEY]
curr = curr[self._PREV]
def __repr__(self): def __repr__(self):
return str(self._items)
def __str__(self):
return str(dict(self._items))
def _get_value(self, key, item):
"""Return the key:value or value:key relationship
for the given item. """
if key == item[0]:
return item[1]
if key == item[1]:
return item[0]
def _find_item(self, key):
"""Search for the item which contains the given key.
This method will compare the key both with the key and the value
of the item until it finds a match else it will raise a KeyError
exception.
Returns:
item (tuple): Tuple which contains the (key, value).
Raises:
KeyError
"""
for item in self._items:
if key == item[0] or key == item[1]:
return item
return '%s(%r)' % (self.__class__.__name__, self.items())
def __eq__(self, other):
if isinstance(other, self.__class__):
return self.items() == other.items()
return False
raise KeyError(key)
def __ne__(self, other):
return not self == other
def _load_into_dict(self, args, kwargs):
"""Load new items into the dictionary. This method handles the
items insertion for the __init__ and update methods. """
def _remove_mapped_key(self, key):
"""Remove the given key both from the linked list
and the map dictionary. """
prev, __, next = self._items_map.pop(key)
prev[self._NEXT] = next
next[self._PREV] = prev
def _load(self, args, kwargs):
"""Load items into our dictionary. """
for item in args: for item in args:
if type(item) == dict: if type(item) == dict:
item = item.items()
item = item.iteritems()
for key, value in item: for key, value in item:
self.__setitem__(key, value)
self[key] = value
for key, value in kwargs.items(): for key, value in kwargs.items():
self.__setitem__(key, value)
self[key] = value
def items(self): def items(self):
"""Return the item list instead of returning the dict view. """
return self._items
return [(key, self[key]) for key in self]
def values(self): def values(self):
"""Return a list with all the values of the dictionary instead of
returning the dict view for the values. """
return [item[1] for item in self._items]
return [self[key] for key in self]
def keys(self): def keys(self):
"""Return a list with all the keys of the dictionary instead of
returning the dict view for the keys. """
return [item[0] for item in self._items]
def get(self, key, default=None):
"""Return the value for key or the key for value if key
is in the dictionary else default.
Note:
This method does NOT raise a KeyError.
"""
try:
return self.__getitem__(key)
except KeyError:
return default
return list(self)
def pop(self, key, default=RANDOM_OBJECT):
"""If key is in the dictionary remove it and return its value
else return default. If default is not given and key is not in
the dictionary a KeyError is raised. """
def pop(self, key, default=_RANDOM_OBJECT):
try: try:
item = self._find_item(key)
value = self._get_value(key, item)
value = self[key]
self._items.remove(item)
del self[key]
except KeyError as error: except KeyError as error:
if default == RANDOM_OBJECT:
if default == _RANDOM_OBJECT:
raise error raise error
value = default value = default
return value return value
def popitem(self):
def popitem(self, last=True):
"""Remove and return a (key, value) pair from the dictionary. """Remove and return a (key, value) pair from the dictionary.
If the dictionary is empty calling popitem() raises a KeyError. If the dictionary is empty calling popitem() raises a KeyError.
Args:
last (bool): When False popitem() will remove the first item
from the list.
Note: Note:
popitem() is useful to destructively iterate over a dictionary. popitem() is useful to destructively iterate over a dictionary.
Raises: Raises:
KeyError KeyError
""" """
if len(self._items) == 0:
if not self:
raise KeyError('popitem(): dictionary is empty') raise KeyError('popitem(): dictionary is empty')
return self._items.pop()
if last:
__, key, __ = self._items[self._PREV]
else:
__, key, __ = self._items[self._NEXT]
value = self.pop(key)
return key, value
def update(self, *args, **kwargs):
self._load(args, kwargs)
def setdefault(self, key, default=None): def setdefault(self, key, default=None):
"""If key is in the dictionary return its value else
insert a new key with a value of default and return default. """
try: try:
return self.__getitem__(key)
return self[key]
except KeyError: except KeyError:
self.__setitem__(key, default)
self[key] = default
return default return default
def update(self, *args, **kwargs):
"""Update the dictionary with the (key, value) pairs
overwriting existing keys.
Example:
>>d = TwoWayOrderedDict(a=1, b=2)
>>print d
{'a': 1, 'b': 2}
>>d.update({'a': 0, 'b': 1, 'c': 2})
{'a': 0, 'b': 1, 'c': 2}
>>d.update(d=3)
{'a': 0, 'b': 1, 'c': 2, 'd': 3}
"""
self._load_into_dict(args, kwargs)
def copy(self): def copy(self):
"""Return a copy of our custom dictionary. """
return TwoWayOrderedDict(self._items)
return self.__class__(self.items())
def clear(self): def clear(self):
"""Remove all items from the dictionary. """
del self._items[:]
self._items = item = []
self._items += [item, None, item]
self._items_map = {}
dict.clear(self)
Loading…
Cancel
Save