def test_setitem(self): q = IndexedList(['a', 'b'], {'a': 123}, 'a', 123, ['a', 'b', 'c'], ['b', 123], ['a'], {'b': 321}) q[0] = 2 self.assertEqual( q._locator, { 'b': SortedList([5, 7]), 'a': SortedList([1, 4, 6]), str: SortedList([0, 2, 3]) }) self.assertTrue(q.is_consistent()) q[2] = ['b', 'c', 'd'] self.assertEqual( q._locator, { 'b': SortedList([2, 5, 7]), 'a': SortedList([1, 4, 6]), str: SortedList([0, 3]) }) self.assertTrue(q.is_consistent()) q[1] = ['cd', 'efg', 12] self.assertEqual( q._locator, { 'a': SortedList([4, 6]), 'b': SortedList([2, 5, 7]), 'cd': SortedList([1]), str: SortedList([0, 3]) }) self.assertTrue(q.is_consistent())
def test_creating_indexedlist2(self): q = IndexedList(["a", "b"], {"a": 123}, "a", 123, ["a", "b", "c"], ["b", 123], ["a"], {"b": 321}) self.assertEqual( repr(q), "IndexedList([['a', 'b'], {'a': 123}, 'a', 123, ['a', 'b', 'c'], ['b', 123], ['a'], {'b': 321}])" ) self.assertEqual(eval(repr(q)), q) self.assertEqual(q._locator, {"b": SortedList([5, 7]), "a": SortedList([0, 1, 4, 6]), str: SortedList([2, 3])}) self.assertTrue(q.is_consistent())
def test_delitem(self): q = IndexedList(["a", "b"], {"a": 123}, "a", 123, ["a", "b", "c"], ["b", 123], ["a"], {"b": 321}) del q[0] self.assertEqual(q._locator, {"b": SortedList([4, 6]), "a": SortedList([0, 3, 5]), str: SortedList([1, 2])}) self.assertTrue(q.is_consistent()) del q[2] self.assertEqual(q._locator, {"b": SortedList([3, 5]), "a": SortedList([0, 2, 4]), str: SortedList([1])}) self.assertTrue(q.is_consistent()) del q[3] self.assertEqual(q._locator, {"b": SortedList([4]), "a": SortedList([0, 2, 3]), str: SortedList([1])}) self.assertTrue(q.is_consistent()) del q[0] self.assertTrue(q.is_consistent()) del q[3] self.assertTrue(q.is_consistent()) del q[0] self.assertTrue(q.is_consistent()) del q[1] self.assertTrue(q.is_consistent()) del q[0] self.assertTrue(q.is_consistent()) self.assertEqual(q, [])
def test_creating_indexedlist2(self): q = IndexedList(['a', 'b'], {'a': 123}, 'a', 123, ['a', 'b', 'c'], ['b', 123], ['a'], {'b': 321}) self.assertEqual( repr(q), "IndexedList([['a', 'b'], {'a': 123}, 'a', 123, ['a', 'b', 'c'], ['b', 123], ['a'], {'b': 321}])" ) self.assertEqual(eval(repr(q)), q) self.assertEqual( q._locator, { 'b': SortedList([5, 7]), 'a': SortedList([0, 1, 4, 6]), str: SortedList([2, 3]) }) self.assertTrue(q.is_consistent())
def test_delitem(self): q = IndexedList(['a', 'b'], {'a': 123}, 'a', 123, ['a', 'b', 'c'], ['b', 123], ['a'], {'b': 321}) del q[0] self.assertEqual( q._locator, { 'b': SortedList([4, 6]), 'a': SortedList([0, 3, 5]), str: SortedList([1, 2]) }) self.assertTrue(q.is_consistent()) del q[2] self.assertEqual( q._locator, { 'b': SortedList([3, 5]), 'a': SortedList([0, 2, 4]), str: SortedList([1]) }) self.assertTrue(q.is_consistent()) del q[3] self.assertEqual(q._locator, { 'b': SortedList([4]), 'a': SortedList([0, 2, 3]), str: SortedList([1]) }) self.assertTrue(q.is_consistent()) del q[0] self.assertTrue(q.is_consistent()) del q[3] self.assertTrue(q.is_consistent()) del q[0] self.assertTrue(q.is_consistent()) del q[1] self.assertTrue(q.is_consistent()) del q[0] self.assertTrue(q.is_consistent()) self.assertEqual(q, [])
def test_setitem(self): q = IndexedList(["a", "b"], {"a": 123}, "a", 123, ["a", "b", "c"], ["b", 123], ["a"], {"b": 321}) q[0] = 2 self.assertEqual(q._locator, {"b": SortedList([5, 7]), "a": SortedList([1, 4, 6]), str: SortedList([0, 2, 3])}) self.assertTrue(q.is_consistent()) q[2] = ["b", "c", "d"] self.assertEqual(q._locator, {"b": SortedList([2, 5, 7]), "a": SortedList([1, 4, 6]), str: SortedList([0, 3])}) self.assertTrue(q.is_consistent()) q[1] = ["cd", "efg", 12] self.assertEqual( q._locator, {"a": SortedList([4, 6]), "b": SortedList([2, 5, 7]), "cd": SortedList([1]), str: SortedList([0, 3])}, ) self.assertTrue(q.is_consistent())
def __init__(self, name, children=None, parent=None, my_index=None): if isinstance(name, dict) and len(name) == 1: self.__init__(*list(name.items())[0], parent=parent) return self.name = name self.parent = parent self.my_index = my_index if children is None: self._children = IndexedList() elif isinstance(children, str) or isinstance(children, int) or isinstance(children, float): self._children = IndexedList([children]) elif isinstance(children, Sequence): self._children = IndexedList(children) else: raise QqError("I don't know what to do with children " + str(children)) for i, child in enumerate(self): if isinstance(child, QqTag): child.parent = self child.my_index = i
def test_insert(self): q = IndexedList(["a", "b"], {"a": 123}, "a", 123, ["a", "b", "c"], ["b", 123], ["a"], {"b": 321}) q.insert(2, "b") self.assertEqual( q._locator, {"a": SortedList([0, 1, 5, 7]), "b": SortedList([6, 8]), str: SortedList([2, 3, 4])} ) self.assertTrue(q.is_consistent()) q.insert(0, ["b", 123]) self.assertEqual( q._locator, {"a": SortedList([1, 2, 6, 8]), "b": SortedList([0, 7, 9]), str: SortedList([3, 4, 5])} ) self.assertTrue(q.is_consistent())
def test_insert(self): q = IndexedList(['a', 'b'], {'a': 123}, 'a', 123, ['a', 'b', 'c'], ['b', 123], ['a'], {'b': 321}) q.insert(2, 'b') self.assertEqual( q._locator, { 'a': SortedList([0, 1, 5, 7]), 'b': SortedList([6, 8]), str: SortedList([2, 3, 4]) }) self.assertTrue(q.is_consistent()) q.insert(0, ['b', 123]) self.assertEqual( q._locator, { 'a': SortedList([1, 2, 6, 8]), 'b': SortedList([0, 7, 9]), str: SortedList([3, 4, 5]) }) self.assertTrue(q.is_consistent())
def __init__(self, name, children=None, parent=None, my_index=None): if isinstance(name, dict) and len(name) == 1: self.__init__(*list(name.items())[0], parent=parent) return self.name = name self.parent = parent self.my_index = my_index if children is None: self._children = IndexedList() elif isinstance(children, str) or isinstance( children, int) or isinstance(children, float): self._children = IndexedList([children]) elif isinstance(children, Sequence): self._children = IndexedList(children) else: raise QqError("I don't know what to do with children " + str(children)) for i, child in enumerate(self): if isinstance(child, QqTag): child.parent = self child.my_index = i
def test_creating_indexedlist1(self): q = IndexedList([['a', 'b'], ['a', 'd']]) self.assertEqual(list(q._locator['a']), [0, 1]) self.assertTrue(q.is_consistent())
def test_creating_indexedlist1(self): q = IndexedList([["a", "b"], ["a", "d"]]) self.assertEqual(list(q._locator["a"]), [0, 1]) self.assertTrue(q.is_consistent())
class QqTag(MutableSequence): """ QqTag is essentially an IndexedList with name attached. It behaves mostly like eTree Element. It provides eTree and BeautifulSoup-style navigation over its child: - ``tag.find('subtag')`` returns first occurrence of a child with name ``subtag``. (Note that in contrast with BeautifulSoup, this is not recursive: it searches only through tag's direct childrens.) - ``tag._subtag`` is a shortcut for ``tag.find('subtag')`` (works if ``subtag`` is valid identifier) - ``tag.find_all('subtag')`` returns all occurrences of tag with name 'subtag' - ``tag('subtag')`` is shortcut for ``tag.find_all('subtag')`` If QqTag has only one child, it is called *simple*. Then its `.value` is defined. (Useful for access to property-like subtags.) """ def __init__(self, name, children=None, parent=None, my_index=None): if isinstance(name, dict) and len(name) == 1: self.__init__(*list(name.items())[0], parent=parent) return self.name = name self.parent = parent self.my_index = my_index if children is None: self._children = IndexedList() elif isinstance(children, str) or isinstance(children, int) or isinstance(children, float): self._children = IndexedList([children]) elif isinstance(children, Sequence): self._children = IndexedList(children) else: raise QqError("I don't know what to do with children " + str(children)) for i, child in enumerate(self): if isinstance(child, QqTag): child.parent = self child.my_index = i def __repr__(self): if self.parent is None: return "QqTag(%s, %s)" % (repr(self.name), repr(self._children)) else: return "QqTag(%s, %s, parent = %s)" % (repr(self.name), repr(self._children), repr(self.parent.name)) def __str__(self): return "{%s : %s}" % (self.name, self._children) def __eq__(self, other): if other is None or not isinstance(other, QqTag): return False return self.as_list() == other.as_list() @property def is_simple(self): """ Simple tags are those containing only one child :return: """ return len(self) == 1 @property def value(self): if self.is_simple: return self[0].strip() raise QqError("More than one child, value is not defined, QqTag: %s" % str(self)) @value.setter def value(self, value): if self.is_simple: self[0] = value else: raise QqError("More than one child, cannot set value") def __qqkey__(self): return self.name def __getattr__(self, attr): if attr[0] == "_": return self.find(attr[1:]) raise AttributeError("Attribute " + attr + " not found") def find(self, key): """ Returns direct children with the given key if it exists, otherwise returns None :param key: key :return: QqTag """ if key in self._children._locator: return self._children.find(key) def find_all(self, key): if key in self._children._locator: return self._children.find_all(key) def __call__(self, key): return self.find_all(key) def as_list(self): ret = [self.name] for child in self: if isinstance(child, QqTag): ret.append(child.as_list()) else: ret.append(child) return ret def insert(self, index: int, child) -> None: self._children.insert(index, child) child.parent = self child.my_index = index for i in range(index+1, len(self)): self._children[i].my_index += 1 def __delitem__(self, index: int): del self._children[index] for i in range(index, len(self)): self._children[i].my_index -= 1 def append_child(self, child): self.insert(len(self), child) def _is_consistent(self): for i, child in enumerate(self): if child.parent != self or child.my_index != i: return False return True def append_line(self, line): if line: self._children.append(line) def __getitem__(self, item): return self._children[item] def __setitem__(self, index, child): self._children[index] = child child.parent = self child.my_index = index def __iter__(self): return iter(self._children) def __len__(self): return len(self._children) @property def text_content(self): chunk = [] for child in self: if isinstance(child, str): chunk.append(child) return "".join(chunk) def exists(self, key): """ Returns True if a child with given key exists :param key: :return: """ return key in self._children._locator def get(self, key, default_value=None): """ Returns a value of a direct child with a given key. If it is does not exists or is not simple, returns default value (default: None) :param key: key :param default_value: what to return if there is no such key or the corresponding child is ot simple :return: the value of a child """ tag = self.find(key) if tag and tag.is_simple: return tag.value else: return default_value def ancestor_path(self): """ Returns list of ancestors for self. Example: \tag \othertag \thirdtag thirdtag.ancestor_path == [thirdtag, othertag, tag, _root] :return: """ tag = self path = [tag] while tag.parent: tag = tag.parent path.append(tag) return path def get_granny(self): """ Returns ancestor which is direct child of root :return: """ return self.ancestor_path()[-2] def next(self): if not self.parent or self.my_index is None or self.my_index == len(self.parent) - 1: return None return self.parent[self.my_index + 1] def prev(self): if not self.parent or self.my_index is None or self.my_index == 0: return None return self.parent[self.my_index - 1] def split_by_sep(self, separator='separator'): chunks = [] chunk = [] sep = QqTag(separator) for child in self: if child == sep: chunks.append(chunk) chunk = [] else: chunk.append(child) chunks.append(chunk) return chunks
class QqTag(MutableSequence): """ QqTag is essentially an IndexedList with name attached. It behaves mostly like eTree Element. It provides eTree and BeautifulSoup-style navigation over its child: - ``tag.find('subtag')`` returns first occurrence of a child with name ``subtag``. (Note that in contrast with BeautifulSoup, this is not recursive: it searches only through tag's direct childrens.) - ``tag._subtag`` is a shortcut for ``tag.find('subtag')`` (works if ``subtag`` is valid identifier) - ``tag.find_all('subtag')`` returns all occurrences of tag with name 'subtag' - ``tag('subtag')`` is shortcut for ``tag.find_all('subtag')`` If QqTag has only one child, it is called *simple*. Then its `.value` is defined. (Useful for access to property-like subtags.) """ def __init__(self, name, children=None, parent=None, my_index=None): if isinstance(name, dict) and len(name) == 1: self.__init__(*list(name.items())[0], parent=parent) return self.name = name self.parent = parent self.my_index = my_index if children is None: self._children = IndexedList() elif isinstance(children, str) or isinstance( children, int) or isinstance(children, float): self._children = IndexedList([children]) elif isinstance(children, Sequence): self._children = IndexedList(children) else: raise QqError("I don't know what to do with children " + str(children)) for i, child in enumerate(self): if isinstance(child, QqTag): child.parent = self child.my_index = i def __repr__(self): if self.parent is None: return "QqTag(%s, %s)" % (repr(self.name), repr(self._children)) else: return "QqTag(%s, %s, parent = %s)" % (repr( self.name), repr(self._children), repr(self.parent.name)) def __str__(self): return "{%s : %s}" % (self.name, self._children) def __eq__(self, other): if other is None or not isinstance(other, QqTag): return False return self.as_list() == other.as_list() @property def is_simple(self): """ Simple tags are those containing only one child :return: """ return len(self) == 1 @property def value(self): if self.is_simple: return self[0].strip() raise QqError("More than one child, value is not defined, QqTag: %s" % str(self)) @value.setter def value(self, value): if self.is_simple: self[0] = value else: raise QqError("More than one child, cannot set value") def __qqkey__(self): return self.name def __getattr__(self, attr): if attr[0] == "_": return self.find(attr[1:]) raise AttributeError("Attribute " + attr + " not found") def find(self, key): """ Returns direct children with the given key if it exists, otherwise returns None :param key: key :return: QqTag """ if key in self._children._locator: return self._children.find(key) def find_all(self, key): if key in self._children._locator: return self._children.find_all(key) def __call__(self, key): return self.find_all(key) def as_list(self): ret = [self.name] for child in self: if isinstance(child, QqTag): ret.append(child.as_list()) else: ret.append(child) return ret def insert(self, index: int, child) -> None: self._children.insert(index, child) child.parent = self child.my_index = index for i in range(index + 1, len(self)): self._children[i].my_index += 1 def __delitem__(self, index: int): del self._children[index] for i in range(index, len(self)): self._children[i].my_index -= 1 def append_child(self, child): self.insert(len(self), child) def _is_consistent(self): for i, child in enumerate(self): if child.parent != self or child.my_index != i: return False return True def append_line(self, line): if line: self._children.append(line) def __getitem__(self, item): return self._children[item] def __setitem__(self, index, child): self._children[index] = child child.parent = self child.my_index = index def __iter__(self): return iter(self._children) def __len__(self): return len(self._children) @property def text_content(self): chunk = [] for child in self: if isinstance(child, str): chunk.append(child) return "".join(chunk) def exists(self, key): """ Returns True if a child with given key exists :param key: :return: """ return key in self._children._locator def get(self, key, default_value=None): """ Returns a value of a direct child with a given key. If it is does not exists or is not simple, returns default value (default: None) :param key: key :param default_value: what to return if there is no such key or the corresponding child is ot simple :return: the value of a child """ tag = self.find(key) if tag and tag.is_simple: return tag.value else: return default_value def ancestor_path(self): """ Returns list of ancestors for self. Example: \tag \othertag \thirdtag thirdtag.ancestor_path == [thirdtag, othertag, tag, _root] :return: """ tag = self path = [tag] while tag.parent: tag = tag.parent path.append(tag) return path def get_granny(self): """ Returns ancestor which is direct child of root :return: """ return self.ancestor_path()[-2] def next(self): if not self.parent or self.my_index is None or self.my_index == len( self.parent) - 1: return None return self.parent[self.my_index + 1] def prev(self): if not self.parent or self.my_index is None or self.my_index == 0: return None return self.parent[self.my_index - 1] def split_by_sep(self, separator='separator'): chunks = [] chunk = [] sep = QqTag(separator) for child in self: if child == sep: chunks.append(chunk) chunk = [] else: chunk.append(child) chunks.append(chunk) return chunks