From 429ad00b6203e19ad88bc334138151034ef61420 Mon Sep 17 00:00:00 2001 From: Isaac Muse Date: Sun, 30 Dec 2018 22:13:49 -0700 Subject: Add convienances for inserting multiple tags Add extend method to append a list of tags. Make insert_before and insert_after accept multiple arguments --- bs4/element.py | 74 ++++++++++++++++++++++++++++---------------------- bs4/tests/test_tree.py | 28 +++++++++++++++++++ 2 files changed, 70 insertions(+), 32 deletions(-) diff --git a/bs4/element.py b/bs4/element.py index fb74f9c..c959c3e 100644 --- a/bs4/element.py +++ b/bs4/element.py @@ -440,43 +440,53 @@ class PageElement(object): """Appends the given tag to the contents of this tag.""" self.insert(len(self.contents), tag) - def insert_before(self, predecessor): - """Makes the given element the immediate predecessor of this one. + def extend(self, tags): + """Appends the given tags to the contents of this tag.""" + for tag in tags: + self.append(tag) - The two elements will have the same parent, and the given element + def insert_before(self, *args): + """Makes the given element(s) the immediate predecessor of this one. + + The elements will have the same parent, and the given elements will be immediately before this one. """ - if self is predecessor: - raise ValueError("Can't insert an element before itself.") - parent = self.parent - if parent is None: - raise ValueError( - "Element has no parent, so 'before' has no meaning.") - # Extract first so that the index won't be screwed up if they - # are siblings. - if isinstance(predecessor, PageElement): - predecessor.extract() - index = parent.index(self) - parent.insert(index, predecessor) - - def insert_after(self, successor): - """Makes the given element the immediate successor of this one. - - The two elements will have the same parent, and the given element + for predecessor in args: + if self is predecessor: + raise ValueError("Can't insert an element before itself.") + parent = self.parent + if parent is None: + raise ValueError( + "Element has no parent, so 'before' has no meaning.") + # Extract first so that the index won't be screwed up if they + # are siblings. + if isinstance(predecessor, PageElement): + predecessor.extract() + index = parent.index(self) + parent.insert(index, predecessor) + + def insert_after(self, *args): + """Makes the given element(s) the immediate successor of this one. + + The elements will have the same parent, and the given elements will be immediately after this one. """ - if self is successor: - raise ValueError("Can't insert an element after itself.") - parent = self.parent - if parent is None: - raise ValueError( - "Element has no parent, so 'after' has no meaning.") - # Extract first so that the index won't be screwed up if they - # are siblings. - if isinstance(successor, PageElement): - successor.extract() - index = parent.index(self) - parent.insert(index+1, successor) + + offset = 0 + for successor in args: + if self is successor: + raise ValueError("Can't insert an element after itself.") + parent = self.parent + if parent is None: + raise ValueError( + "Element has no parent, so 'after' has no meaning.") + # Extract first so that the index won't be screwed up if they + # are siblings. + if isinstance(successor, PageElement): + successor.extract() + index = parent.index(self) + parent.insert(index+1+offset, successor) + offset += 1 def find_next(self, name=None, attrs={}, text=None, **kwargs): """Returns the first item that matches the given criteria and diff --git a/bs4/tests/test_tree.py b/bs4/tests/test_tree.py index f1af6ce..e6c07aa 100644 --- a/bs4/tests/test_tree.py +++ b/bs4/tests/test_tree.py @@ -931,6 +931,13 @@ class TestTreeModification(SoupTest): soup.a.append(soup.b) self.assertEqual(data, soup.decode()) + def test_extend(self): + data = "" + soup = self.soup(data) + l = [soup.g, soup.f, soup.e, soup.d, soup.c, soup.b] + soup.a.extend(l) + self.assertEqual("", soup.decode()) + def test_move_tag_to_beginning_of_parent(self): data = "" soup = self.soup(data) @@ -957,6 +964,17 @@ class TestTreeModification(SoupTest): self.assertEqual( soup.decode(), self.document_for("QUUXbarfooBAZ")) + def test_insert_multiple_before(self): + soup = self.soup("foobar") + soup.b.insert_before("BAZ", " ", "QUUX") + soup.a.insert_before("QUUX", " ", "BAZ") + self.assertEqual( + soup.decode(), self.document_for("QUUX BAZfooBAZ QUUXbar")) + + soup.a.insert_before(soup.b, "FOO") + self.assertEqual( + soup.decode(), self.document_for("QUUX BAZbarFOOfooBAZ QUUX")) + def test_insert_after(self): soup = self.soup("foobar") soup.b.insert_after("BAZ") @@ -967,6 +985,16 @@ class TestTreeModification(SoupTest): self.assertEqual( soup.decode(), self.document_for("QUUXbarfooBAZ")) + def test_insert_multiple_after(self): + soup = self.soup("foobar") + soup.b.insert_after("BAZ", " ", "QUUX") + soup.a.insert_after("QUUX", " ", "BAZ") + self.assertEqual( + soup.decode(), self.document_for("fooQUUX BAZbarBAZ QUUX")) + soup.b.insert_after(soup.a, "FOO ") + self.assertEqual( + soup.decode(), self.document_for("QUUX BAZbarfooFOO BAZ QUUX")) + def test_insert_after_raises_exception_if_after_has_no_meaning(self): soup = self.soup("") tag = soup.new_tag("a") -- cgit v1.2.1