diff options
Diffstat (limited to '.venv/lib/python3.12/site-packages/bs4/css.py')
-rw-r--r-- | .venv/lib/python3.12/site-packages/bs4/css.py | 338 |
1 files changed, 338 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/bs4/css.py b/.venv/lib/python3.12/site-packages/bs4/css.py new file mode 100644 index 00000000..c20850fc --- /dev/null +++ b/.venv/lib/python3.12/site-packages/bs4/css.py @@ -0,0 +1,338 @@ +"""Integration code for CSS selectors using `Soup Sieve <https://facelessuser.github.io/soupsieve/>`_ (pypi: ``soupsieve``). + +Acquire a `CSS` object through the `element.Tag.css` attribute of +the starting point of your CSS selector, or (if you want to run a +selector against the entire document) of the `BeautifulSoup` object +itself. + +The main advantage of doing this instead of using ``soupsieve`` +functions is that you don't need to keep passing the `element.Tag` to be +selected against, since the `CSS` object is permanently scoped to that +`element.Tag`. + +""" + +from __future__ import annotations + +from types import ModuleType +from typing import ( + Any, + cast, + Iterable, + Iterator, + Optional, + TYPE_CHECKING, +) +import warnings +from bs4._typing import _NamespaceMapping + +if TYPE_CHECKING: + from soupsieve import SoupSieve + from bs4 import element + from bs4.element import ResultSet, Tag + +soupsieve: Optional[ModuleType] +try: + import soupsieve +except ImportError: + soupsieve = None + warnings.warn( + "The soupsieve package is not installed. CSS selectors cannot be used." + ) + + +class CSS(object): + """A proxy object against the ``soupsieve`` library, to simplify its + CSS selector API. + + You don't need to instantiate this class yourself; instead, use + `element.Tag.css`. + + :param tag: All CSS selectors run by this object will use this as + their starting point. + + :param api: An optional drop-in replacement for the ``soupsieve`` module, + intended for use in unit tests. + """ + + def __init__(self, tag: element.Tag, api: Optional[ModuleType] = None): + if api is None: + api = soupsieve + if api is None: + raise NotImplementedError( + "Cannot execute CSS selectors because the soupsieve package is not installed." + ) + self.api = api + self.tag = tag + + def escape(self, ident: str) -> str: + """Escape a CSS identifier. + + This is a simple wrapper around `soupsieve.escape() <https://facelessuser.github.io/soupsieve/api/#soupsieveescape>`_. See the + documentation for that function for more information. + """ + if soupsieve is None: + raise NotImplementedError( + "Cannot escape CSS identifiers because the soupsieve package is not installed." + ) + return cast(str, self.api.escape(ident)) + + def _ns( + self, ns: Optional[_NamespaceMapping], select: str + ) -> Optional[_NamespaceMapping]: + """Normalize a dictionary of namespaces.""" + if not isinstance(select, self.api.SoupSieve) and ns is None: + # If the selector is a precompiled pattern, it already has + # a namespace context compiled in, which cannot be + # replaced. + ns = self.tag._namespaces + return ns + + def _rs(self, results: Iterable[Tag]) -> ResultSet[Tag]: + """Normalize a list of results to a py:class:`ResultSet`. + + A py:class:`ResultSet` is more consistent with the rest of + Beautiful Soup's API, and :py:meth:`ResultSet.__getattr__` has + a helpful error message if you try to treat a list of results + as a single result (a common mistake). + """ + # Import here to avoid circular import + from bs4 import ResultSet + + return ResultSet(None, results) + + def compile( + self, + select: str, + namespaces: Optional[_NamespaceMapping] = None, + flags: int = 0, + **kwargs: Any, + ) -> SoupSieve: + """Pre-compile a selector and return the compiled object. + + :param selector: A CSS selector. + + :param namespaces: A dictionary mapping namespace prefixes + used in the CSS selector to namespace URIs. By default, + Beautiful Soup will use the prefixes it encountered while + parsing the document. + + :param flags: Flags to be passed into Soup Sieve's + `soupsieve.compile() <https://facelessuser.github.io/soupsieve/api/#soupsievecompile>`_ method. + + :param kwargs: Keyword arguments to be passed into Soup Sieve's + `soupsieve.compile() <https://facelessuser.github.io/soupsieve/api/#soupsievecompile>`_ method. + + :return: A precompiled selector object. + :rtype: soupsieve.SoupSieve + """ + return self.api.compile(select, self._ns(namespaces, select), flags, **kwargs) + + def select_one( + self, + select: str, + namespaces: Optional[_NamespaceMapping] = None, + flags: int = 0, + **kwargs: Any, + ) -> element.Tag | None: + """Perform a CSS selection operation on the current Tag and return the + first result, if any. + + This uses the Soup Sieve library. For more information, see + that library's documentation for the `soupsieve.select_one() <https://facelessuser.github.io/soupsieve/api/#soupsieveselect_one>`_ method. + + :param selector: A CSS selector. + + :param namespaces: A dictionary mapping namespace prefixes + used in the CSS selector to namespace URIs. By default, + Beautiful Soup will use the prefixes it encountered while + parsing the document. + + :param flags: Flags to be passed into Soup Sieve's + `soupsieve.select_one() <https://facelessuser.github.io/soupsieve/api/#soupsieveselect_one>`_ method. + + :param kwargs: Keyword arguments to be passed into Soup Sieve's + `soupsieve.select_one() <https://facelessuser.github.io/soupsieve/api/#soupsieveselect_one>`_ method. + """ + return self.api.select_one( + select, self.tag, self._ns(namespaces, select), flags, **kwargs + ) + + def select( + self, + select: str, + namespaces: Optional[_NamespaceMapping] = None, + limit: int = 0, + flags: int = 0, + **kwargs: Any, + ) -> ResultSet[element.Tag]: + """Perform a CSS selection operation on the current `element.Tag`. + + This uses the Soup Sieve library. For more information, see + that library's documentation for the `soupsieve.select() <https://facelessuser.github.io/soupsieve/api/#soupsieveselect>`_ method. + + :param selector: A CSS selector. + + :param namespaces: A dictionary mapping namespace prefixes + used in the CSS selector to namespace URIs. By default, + Beautiful Soup will pass in the prefixes it encountered while + parsing the document. + + :param limit: After finding this number of results, stop looking. + + :param flags: Flags to be passed into Soup Sieve's + `soupsieve.select() <https://facelessuser.github.io/soupsieve/api/#soupsieveselect>`_ method. + + :param kwargs: Keyword arguments to be passed into Soup Sieve's + `soupsieve.select() <https://facelessuser.github.io/soupsieve/api/#soupsieveselect>`_ method. + """ + if limit is None: + limit = 0 + + return self._rs( + self.api.select( + select, self.tag, self._ns(namespaces, select), limit, flags, **kwargs + ) + ) + + def iselect( + self, + select: str, + namespaces: Optional[_NamespaceMapping] = None, + limit: int = 0, + flags: int = 0, + **kwargs: Any, + ) -> Iterator[element.Tag]: + """Perform a CSS selection operation on the current `element.Tag`. + + This uses the Soup Sieve library. For more information, see + that library's documentation for the `soupsieve.iselect() + <https://facelessuser.github.io/soupsieve/api/#soupsieveiselect>`_ + method. It is the same as select(), but it returns a generator + instead of a list. + + :param selector: A string containing a CSS selector. + + :param namespaces: A dictionary mapping namespace prefixes + used in the CSS selector to namespace URIs. By default, + Beautiful Soup will pass in the prefixes it encountered while + parsing the document. + + :param limit: After finding this number of results, stop looking. + + :param flags: Flags to be passed into Soup Sieve's + `soupsieve.iselect() <https://facelessuser.github.io/soupsieve/api/#soupsieveiselect>`_ method. + + :param kwargs: Keyword arguments to be passed into Soup Sieve's + `soupsieve.iselect() <https://facelessuser.github.io/soupsieve/api/#soupsieveiselect>`_ method. + """ + return self.api.iselect( + select, self.tag, self._ns(namespaces, select), limit, flags, **kwargs + ) + + def closest( + self, + select: str, + namespaces: Optional[_NamespaceMapping] = None, + flags: int = 0, + **kwargs: Any, + ) -> Optional[element.Tag]: + """Find the `element.Tag` closest to this one that matches the given selector. + + This uses the Soup Sieve library. For more information, see + that library's documentation for the `soupsieve.closest() + <https://facelessuser.github.io/soupsieve/api/#soupsieveclosest>`_ + method. + + :param selector: A string containing a CSS selector. + + :param namespaces: A dictionary mapping namespace prefixes + used in the CSS selector to namespace URIs. By default, + Beautiful Soup will pass in the prefixes it encountered while + parsing the document. + + :param flags: Flags to be passed into Soup Sieve's + `soupsieve.closest() <https://facelessuser.github.io/soupsieve/api/#soupsieveclosest>`_ method. + + :param kwargs: Keyword arguments to be passed into Soup Sieve's + `soupsieve.closest() <https://facelessuser.github.io/soupsieve/api/#soupsieveclosest>`_ method. + + """ + return self.api.closest( + select, self.tag, self._ns(namespaces, select), flags, **kwargs + ) + + def match( + self, + select: str, + namespaces: Optional[_NamespaceMapping] = None, + flags: int = 0, + **kwargs: Any, + ) -> bool: + """Check whether or not this `element.Tag` matches the given CSS selector. + + This uses the Soup Sieve library. For more information, see + that library's documentation for the `soupsieve.match() + <https://facelessuser.github.io/soupsieve/api/#soupsievematch>`_ + method. + + :param: a CSS selector. + + :param namespaces: A dictionary mapping namespace prefixes + used in the CSS selector to namespace URIs. By default, + Beautiful Soup will pass in the prefixes it encountered while + parsing the document. + + :param flags: Flags to be passed into Soup Sieve's + `soupsieve.match() + <https://facelessuser.github.io/soupsieve/api/#soupsievematch>`_ + method. + + :param kwargs: Keyword arguments to be passed into SoupSieve's + `soupsieve.match() + <https://facelessuser.github.io/soupsieve/api/#soupsievematch>`_ + method. + """ + return cast( + bool, + self.api.match( + select, self.tag, self._ns(namespaces, select), flags, **kwargs + ), + ) + + def filter( + self, + select: str, + namespaces: Optional[_NamespaceMapping] = None, + flags: int = 0, + **kwargs: Any, + ) -> ResultSet[element.Tag]: + """Filter this `element.Tag`'s direct children based on the given CSS selector. + + This uses the Soup Sieve library. It works the same way as + passing a `element.Tag` into that library's `soupsieve.filter() + <https://facelessuser.github.io/soupsieve/api/#soupsievefilter>`_ + method. For more information, see the documentation for + `soupsieve.filter() + <https://facelessuser.github.io/soupsieve/api/#soupsievefilter>`_. + + :param namespaces: A dictionary mapping namespace prefixes + used in the CSS selector to namespace URIs. By default, + Beautiful Soup will pass in the prefixes it encountered while + parsing the document. + + :param flags: Flags to be passed into Soup Sieve's + `soupsieve.filter() + <https://facelessuser.github.io/soupsieve/api/#soupsievefilter>`_ + method. + + :param kwargs: Keyword arguments to be passed into SoupSieve's + `soupsieve.filter() + <https://facelessuser.github.io/soupsieve/api/#soupsievefilter>`_ + method. + """ + return self._rs( + self.api.filter( + select, self.tag, self._ns(namespaces, select), flags, **kwargs + ) + ) |