aboutsummaryrefslogtreecommitdiffstats
path: root/bot/cogs/doc/html.py
diff options
context:
space:
mode:
Diffstat (limited to 'bot/cogs/doc/html.py')
-rw-r--r--bot/cogs/doc/html.py33
1 files changed, 33 insertions, 0 deletions
diff --git a/bot/cogs/doc/html.py b/bot/cogs/doc/html.py
new file mode 100644
index 000000000..bc705130d
--- /dev/null
+++ b/bot/cogs/doc/html.py
@@ -0,0 +1,33 @@
+from collections.abc import Iterable
+from typing import List, Union
+
+from bs4.element import NavigableString, PageElement, SoupStrainer, Tag
+
+
+class Strainer(SoupStrainer):
+ """Subclass of SoupStrainer to allow matching of both `Tag`s and `NavigableString`s."""
+
+ def __init__(self, *, include_strings: bool, **kwargs):
+ self.include_strings = include_strings
+ super().__init__(**kwargs)
+
+ markup_hint = Union[PageElement, List["markup_hint"]]
+
+ def search(self, markup: markup_hint) -> Union[PageElement, str]:
+ """Extend default SoupStrainer behaviour to allow matching both `Tag`s` and `NavigableString`s."""
+ if isinstance(markup, Iterable) and not isinstance(markup, (Tag, str)):
+ for element in markup:
+ if isinstance(element, NavigableString) and self.search(element):
+ return element
+ elif isinstance(markup, Tag):
+ # Also include tags while we're searching for strings and tags.
+ if self.include_strings or (not self.text or self.name or self.attrs):
+ return self.search_tag(markup)
+
+ elif isinstance(markup, str):
+ # Let everything through the text filter if we're including strings and tags.
+ text_filter = None if not self.include_strings else True
+ if not self.name and not self.attrs and self._matches(markup, text_filter):
+ return markup
+ else:
+ raise Exception(f"I don't know how to match against a {markup.__class__}")