mirror of
https://github.com/ytdl-org/youtube-dl.git
synced 2025-12-08 07:11:35 +01:00
[compat] Add compat_dict
A dict that preserves insertion order and otherwise resembles the dict builtin (if it isn't it) rather than `collections.OrderedDict`. Also: * compat_builtins_dict: the built-in definition in case `compat_dict` was imported as `dict` * compat_dict_items: use instead of `dict.items` to get items from a `compat_dict` in insertion order, if you didn't define `dict` as `compat_dict`.
This commit is contained in:
@@ -3492,6 +3492,31 @@ except ImportError:
|
||||
compat_abc_ABC = _ABCMeta(str('ABC'), (object,), {})
|
||||
|
||||
|
||||
# dict mixin used here
|
||||
# like UserDict.DictMixin, without methods created by MutableMapping
|
||||
class _DictMixin(compat_abc_ABC):
|
||||
def has_key(self, key):
|
||||
return key in self
|
||||
|
||||
# get(), clear(), setdefault() in MM
|
||||
|
||||
def iterkeys(self):
|
||||
return (k for k in self)
|
||||
|
||||
def itervalues(self):
|
||||
return (self[k] for k in self)
|
||||
|
||||
def iteritems(self):
|
||||
return ((k, self[k]) for k in self)
|
||||
|
||||
# pop(), popitem() in MM
|
||||
|
||||
def copy(self):
|
||||
return type(self)(self)
|
||||
|
||||
# update() in MM
|
||||
|
||||
|
||||
# compat_collections_chain_map
|
||||
# collections.ChainMap: new class
|
||||
try:
|
||||
@@ -3656,6 +3681,119 @@ except ImportError:
|
||||
import dummy_thread as compat_thread
|
||||
|
||||
|
||||
# compat_dict
|
||||
# compat_builtins_dict
|
||||
# compat_dict_items
|
||||
if sys.version_info >= (3, 6):
|
||||
compat_dict = compat_builtins_dict = dict
|
||||
compat_dict_items = dict.items
|
||||
else:
|
||||
_get_ident = compat_thread.get_ident
|
||||
|
||||
class compat_dict(compat_collections_abc.MutableMapping, _DictMixin, dict):
|
||||
"""`dict` that preserves insertion order with interface like Py3.7+"""
|
||||
|
||||
_order = [] # default that should never be used
|
||||
|
||||
def __init__(self, *mappings_or_iterables, **kwargs):
|
||||
# order an unordered dict using a list of keys: actual Py 2.7+
|
||||
# OrderedDict uses a doubly linked list for better performance
|
||||
self._order = []
|
||||
for arg in mappings_or_iterables:
|
||||
self.__update(arg)
|
||||
if kwargs:
|
||||
self.__update(kwargs)
|
||||
|
||||
def __getitem__(self, key):
|
||||
return dict.__getitem__(self, key)
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
try:
|
||||
if key not in self._order:
|
||||
self._order.append(key)
|
||||
dict.__setitem__(self, key, value)
|
||||
except Exception:
|
||||
if key in self._order[-1:] and key not in self:
|
||||
del self._order[-1]
|
||||
raise
|
||||
|
||||
def __len__(self):
|
||||
return dict.__len__(self)
|
||||
|
||||
def __delitem__(self, key):
|
||||
dict.__delitem__(self, key)
|
||||
try:
|
||||
# expected case, O(len(self)), but who dels anyway?
|
||||
self._order.remove(key)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
def __iter__(self):
|
||||
for from_ in self._order:
|
||||
if from_ in self:
|
||||
yield from_
|
||||
|
||||
def __del__(self):
|
||||
for attr in ('_order',):
|
||||
try:
|
||||
delattr(self, attr)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def __repr__(self, _repr_running={}):
|
||||
# skip recursive items ...
|
||||
call_key = id(self), _get_ident()
|
||||
if _repr_running.get(call_key):
|
||||
return '...'
|
||||
_repr_running[call_key] = True
|
||||
try:
|
||||
return '%s({%s})' % (
|
||||
type(self).__name__,
|
||||
','.join('%r: %r' % k_v for k_v in self.items()))
|
||||
finally:
|
||||
del _repr_running[call_key]
|
||||
|
||||
# merge/update (PEP 584)
|
||||
|
||||
def __or__(self, other):
|
||||
if not isinstance(other, compat_collections_abc.Mapping):
|
||||
return NotImplemented
|
||||
new = type(self)(self)
|
||||
new.update(other)
|
||||
return new
|
||||
|
||||
def __ror__(self, other):
|
||||
if not isinstance(other, compat_collections_abc.Mapping):
|
||||
return NotImplemented
|
||||
new = type(other)(other)
|
||||
new.update(self)
|
||||
return new
|
||||
|
||||
def __ior__(self, other):
|
||||
self.update(other)
|
||||
return self
|
||||
|
||||
# optimisations
|
||||
|
||||
def __reversed__(self):
|
||||
for from_ in reversed(self._order):
|
||||
if from_ in self:
|
||||
yield from_
|
||||
|
||||
def __contains__(self, item):
|
||||
return dict.__contains__(self, item)
|
||||
|
||||
# allow overriding update without breaking __init__
|
||||
def __update(self, *args, **kwargs):
|
||||
super(compat_dict, self).update(*args, **kwargs)
|
||||
|
||||
compat_builtins_dict = dict
|
||||
# Using the object's method, not dict's:
|
||||
# an ordered dict's items can be returned unstably by unordered
|
||||
# dict.items as if the method was not ((k, self[k]) for k in self)
|
||||
compat_dict_items = lambda d: d.items()
|
||||
|
||||
|
||||
legacy = [
|
||||
'compat_HTMLParseError',
|
||||
'compat_HTMLParser',
|
||||
@@ -3690,6 +3828,7 @@ __all__ = [
|
||||
'compat_base64_b64decode',
|
||||
'compat_basestring',
|
||||
'compat_brotli',
|
||||
'compat_builtins_dict',
|
||||
'compat_casefold',
|
||||
'compat_chr',
|
||||
'compat_collections_abc',
|
||||
@@ -3697,6 +3836,8 @@ __all__ = [
|
||||
'compat_contextlib_suppress',
|
||||
'compat_ctypes_WINFUNCTYPE',
|
||||
'compat_datetime_timedelta_total_seconds',
|
||||
'compat_dict',
|
||||
'compat_dict_items',
|
||||
'compat_etree_fromstring',
|
||||
'compat_etree_iterfind',
|
||||
'compat_filter',
|
||||
|
||||
Reference in New Issue
Block a user