Source code for galaxy.util.odict

"""
Ordered dictionary implementation with `insert` functionality.

This is only used in one specific place in the codebase:
    galaxy.tool_util.toolbox.panel

Whenever possible the stdlib `collections.OrderedDict` should be used instead of
this custom implementation.
"""

import sys
from collections import UserDict
from typing import (
    Dict,
    Generic,
    List,
    Optional,
    Tuple,
    TypeVar,
    Union,
)

dict_alias = dict

KeyT = TypeVar("KeyT")
ValueT = TypeVar("ValueT")

if sys.version_info >= (3, 9):

    # A simple type alias doesn't work with mypy
    class TypedUserDict(UserDict[KeyT, ValueT]): ...

else:

    # UserDict is not generic in Python < 3.9
    # TypeError: 'ABCMeta' object is not subscriptable
[docs] class TypedUserDict(UserDict, Generic[KeyT, ValueT]): ...
[docs] class odict(TypedUserDict[KeyT, ValueT]): """ http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/107747 This dictionary class extends UserDict to record the order in which items are added. Calling keys(), values(), items(), etc. will return results in this order. """
[docs] def __init__(self, dict: Optional[Union[Dict[KeyT, ValueT], List[Tuple[KeyT, ValueT]]]] = None) -> None: item = dict self._keys: List[KeyT] = [] if isinstance(item, dict_alias): super().__init__(item) else: super().__init__(None) if isinstance(item, list): for key, value in item: self[key] = value
def __delitem__(self, key: KeyT) -> None: super().__delitem__(key) self._keys.remove(key) def __setitem__(self, key: KeyT, item: ValueT) -> None: super().__setitem__(key, item) if key not in self._keys: self._keys.append(key)
[docs] def clear(self) -> None: super().clear() self._keys = []
[docs] def copy(self) -> "odict[KeyT, ValueT]": new: odict[KeyT, ValueT] = odict() new.update(self) return new
[docs] def items(self): return zip(self._keys, self.values())
[docs] def keys(self): return self._keys[:]
[docs] def popitem(self) -> Tuple[KeyT, ValueT]: try: key = self._keys[-1] except IndexError: raise KeyError("dictionary is empty") val = self[key] del self[key] return (key, val)
[docs] def setdefault(self, key, failobj=None): if key not in self._keys: self._keys.append(key) return super().setdefault(key, failobj)
[docs] def values(self): return map(self.get, self._keys)
[docs] def iterkeys(self): return iter(self._keys)
[docs] def itervalues(self): for key in self._keys: yield self.get(key)
[docs] def iteritems(self): for key in self._keys: yield key, self.get(key)
def __iter__(self): yield from self._keys
[docs] def reverse(self): self._keys.reverse()
[docs] def insert(self, index, key: KeyT, item: ValueT) -> None: if key not in self._keys: self._keys.insert(index, key) super().__setitem__(key, item)