mirror of
https://github.com/gryf/ebook-converter.git
synced 2026-02-19 16:25:55 +01:00
Initial import
This commit is contained in:
133
ebook_converter/css_selectors/ordered_set.py
Normal file
133
ebook_converter/css_selectors/ordered_set.py
Normal file
@@ -0,0 +1,133 @@
|
||||
#!/usr/bin/env python2
|
||||
# vim:fileencoding=utf-8
|
||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2015, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
|
||||
import collections
|
||||
from polyglot.builtins import string_or_bytes
|
||||
|
||||
SLICE_ALL = slice(None)
|
||||
|
||||
|
||||
def is_iterable(obj):
|
||||
"""
|
||||
Are we being asked to look up a list of things, instead of a single thing?
|
||||
We check for the `__iter__` attribute so that this can cover types that
|
||||
don't have to be known by this module, such as NumPy arrays.
|
||||
|
||||
Strings, however, should be considered as atomic values to look up, not
|
||||
iterables.
|
||||
"""
|
||||
return hasattr(obj, '__iter__') and not isinstance(obj, string_or_bytes)
|
||||
|
||||
|
||||
class OrderedSet(collections.MutableSet):
|
||||
"""
|
||||
An OrderedSet is a custom MutableSet that remembers its order, so that
|
||||
every entry has an index that can be looked up.
|
||||
"""
|
||||
def __init__(self, iterable=None):
|
||||
self.items = []
|
||||
self.map = {}
|
||||
if iterable is not None:
|
||||
for item in iterable:
|
||||
idx = self.map.get(item)
|
||||
if idx is None:
|
||||
self.map[item] = len(self.items)
|
||||
self.items.append(item)
|
||||
|
||||
def __len__(self):
|
||||
return len(self.items)
|
||||
|
||||
def __getitem__(self, index):
|
||||
"""
|
||||
Get the item at a given index.
|
||||
|
||||
If `index` is a slice, you will get back that slice of items. If it's
|
||||
the slice [:], exactly the same object is returned. (If you want an
|
||||
independent copy of an OrderedSet, use `OrderedSet.copy()`.)
|
||||
|
||||
If `index` is an iterable, you'll get the OrderedSet of items
|
||||
corresponding to those indices. This is similar to NumPy's
|
||||
"fancy indexing".
|
||||
"""
|
||||
if index == SLICE_ALL:
|
||||
return self
|
||||
elif hasattr(index, '__index__') or isinstance(index, slice):
|
||||
result = self.items[index]
|
||||
if isinstance(result, list):
|
||||
return OrderedSet(result)
|
||||
else:
|
||||
return result
|
||||
elif is_iterable(index):
|
||||
return OrderedSet([self.items[i] for i in index])
|
||||
else:
|
||||
raise TypeError("Don't know how to index an OrderedSet by %r" %
|
||||
index)
|
||||
|
||||
def copy(self):
|
||||
return OrderedSet(self)
|
||||
|
||||
def __getstate__(self):
|
||||
return tuple(self)
|
||||
|
||||
def __setstate__(self, state):
|
||||
self.__init__(state)
|
||||
|
||||
def __contains__(self, key):
|
||||
return key in self.map
|
||||
|
||||
def add(self, key):
|
||||
"""
|
||||
Add `key` as an item to this OrderedSet, then return its index.
|
||||
|
||||
If `key` is already in the OrderedSet, return the index it already
|
||||
had.
|
||||
"""
|
||||
index = self.map.get(key)
|
||||
if index is None:
|
||||
self.map[key] = index = len(self.items)
|
||||
self.items.append(key)
|
||||
return index
|
||||
|
||||
def index(self, key):
|
||||
"""
|
||||
Get the index of a given entry, raising an IndexError if it's not
|
||||
present.
|
||||
|
||||
`key` can be an iterable of entries that is not a string, in which case
|
||||
this returns a list of indices.
|
||||
"""
|
||||
if is_iterable(key):
|
||||
return [self.index(subkey) for subkey in key]
|
||||
return self.map[key]
|
||||
|
||||
def discard(self, key):
|
||||
index = self.map.get(key)
|
||||
if index is not None:
|
||||
self.items.pop(index)
|
||||
for item in self.items[index:]:
|
||||
self.map[item] -= 1
|
||||
return True
|
||||
return False
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self.items)
|
||||
|
||||
def __reversed__(self):
|
||||
return reversed(self.items)
|
||||
|
||||
def __repr__(self):
|
||||
if not self:
|
||||
return '%s()' % (self.__class__.__name__,)
|
||||
return '%s(%r)' % (self.__class__.__name__, list(self))
|
||||
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, OrderedSet):
|
||||
return len(self) == len(other) and self.items == other.items
|
||||
try:
|
||||
return type(other)(self.map) == other
|
||||
except TypeError:
|
||||
return False
|
||||
Reference in New Issue
Block a user