Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/bs4/css.py: 43%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""Integration code for CSS selectors using `Soup Sieve <https://facelessuser.github.io/soupsieve/>`_ (pypi: ``soupsieve``).
3Acquire a `CSS` object through the `element.Tag.css` attribute of
4the starting point of your CSS selector, or (if you want to run a
5selector against the entire document) of the `BeautifulSoup` object
6itself.
8The main advantage of doing this instead of using ``soupsieve``
9functions is that you don't need to keep passing the `element.Tag` to be
10selected against, since the `CSS` object is permanently scoped to that
11`element.Tag`.
13"""
15from __future__ import annotations
17from types import ModuleType
18from typing import (
19 Any,
20 cast,
21 Iterable,
22 Iterator,
23 Optional,
24 TYPE_CHECKING,
25)
26import warnings
27from bs4._typing import _NamespaceMapping
29if TYPE_CHECKING:
30 from soupsieve import SoupSieve
31 from bs4 import element
32 from bs4.element import ResultSet, Tag
34soupsieve: Optional[ModuleType]
35try:
36 import soupsieve
37except ImportError:
38 soupsieve = None
39 warnings.warn(
40 "The soupsieve package is not installed. CSS selectors cannot be used."
41 )
44class CSS(object):
45 """A proxy object against the ``soupsieve`` library, to simplify its
46 CSS selector API.
48 You don't need to instantiate this class yourself; instead, use
49 `element.Tag.css`.
51 :param tag: All CSS selectors run by this object will use this as
52 their starting point.
54 :param api: An optional drop-in replacement for the ``soupsieve`` module,
55 intended for use in unit tests.
56 """
58 def __init__(self, tag: element.Tag, api: Optional[ModuleType] = None):
59 if api is None:
60 api = soupsieve
61 if api is None:
62 raise NotImplementedError(
63 "Cannot execute CSS selectors because the soupsieve package is not installed."
64 )
65 self.api = api
66 self.tag = tag
68 def escape(self, ident: str) -> str:
69 """Escape a CSS identifier.
71 This is a simple wrapper around `soupsieve.escape() <https://facelessuser.github.io/soupsieve/api/#soupsieveescape>`_. See the
72 documentation for that function for more information.
73 """
74 if soupsieve is None:
75 raise NotImplementedError(
76 "Cannot escape CSS identifiers because the soupsieve package is not installed."
77 )
78 return cast(str, self.api.escape(ident))
80 def _ns(
81 self, ns: Optional[_NamespaceMapping], select: str
82 ) -> Optional[_NamespaceMapping]:
83 """Normalize a dictionary of namespaces."""
84 if not isinstance(select, self.api.SoupSieve) and ns is None:
85 # If the selector is a precompiled pattern, it already has
86 # a namespace context compiled in, which cannot be
87 # replaced.
88 ns = self.tag._namespaces
89 return ns
91 def _rs(self, results: Iterable[Tag]) -> ResultSet[Tag]:
92 """Normalize a list of results to a py:class:`ResultSet`.
94 A py:class:`ResultSet` is more consistent with the rest of
95 Beautiful Soup's API, and :py:meth:`ResultSet.__getattr__` has
96 a helpful error message if you try to treat a list of results
97 as a single result (a common mistake).
98 """
99 # Import here to avoid circular import
100 from bs4 import ResultSet
102 return ResultSet(None, results)
104 def compile(
105 self,
106 select: str,
107 namespaces: Optional[_NamespaceMapping] = None,
108 flags: int = 0,
109 **kwargs: Any,
110 ) -> SoupSieve:
111 """Pre-compile a selector and return the compiled object.
113 :param selector: A CSS selector.
115 :param namespaces: A dictionary mapping namespace prefixes
116 used in the CSS selector to namespace URIs. By default,
117 Beautiful Soup will use the prefixes it encountered while
118 parsing the document.
120 :param flags: Flags to be passed into Soup Sieve's
121 `soupsieve.compile() <https://facelessuser.github.io/soupsieve/api/#soupsievecompile>`_ method.
123 :param kwargs: Keyword arguments to be passed into Soup Sieve's
124 `soupsieve.compile() <https://facelessuser.github.io/soupsieve/api/#soupsievecompile>`_ method.
126 :return: A precompiled selector object.
127 :rtype: soupsieve.SoupSieve
128 """
129 return self.api.compile(select, self._ns(namespaces, select), flags, **kwargs)
131 def select_one(
132 self,
133 select: str,
134 namespaces: Optional[_NamespaceMapping] = None,
135 flags: int = 0,
136 **kwargs: Any,
137 ) -> element.Tag | None:
138 """Perform a CSS selection operation on the current Tag and return the
139 first result, if any.
141 This uses the Soup Sieve library. For more information, see
142 that library's documentation for the `soupsieve.select_one() <https://facelessuser.github.io/soupsieve/api/#soupsieveselect_one>`_ method.
144 :param selector: A CSS selector.
146 :param namespaces: A dictionary mapping namespace prefixes
147 used in the CSS selector to namespace URIs. By default,
148 Beautiful Soup will use the prefixes it encountered while
149 parsing the document.
151 :param flags: Flags to be passed into Soup Sieve's
152 `soupsieve.select_one() <https://facelessuser.github.io/soupsieve/api/#soupsieveselect_one>`_ method.
154 :param kwargs: Keyword arguments to be passed into Soup Sieve's
155 `soupsieve.select_one() <https://facelessuser.github.io/soupsieve/api/#soupsieveselect_one>`_ method.
156 """
157 return self.api.select_one(
158 select, self.tag, self._ns(namespaces, select), flags, **kwargs
159 )
161 def select(
162 self,
163 select: str,
164 namespaces: Optional[_NamespaceMapping] = None,
165 limit: int = 0,
166 flags: int = 0,
167 **kwargs: Any,
168 ) -> ResultSet[element.Tag]:
169 """Perform a CSS selection operation on the current `element.Tag`.
171 This uses the Soup Sieve library. For more information, see
172 that library's documentation for the `soupsieve.select() <https://facelessuser.github.io/soupsieve/api/#soupsieveselect>`_ method.
174 :param selector: A CSS selector.
176 :param namespaces: A dictionary mapping namespace prefixes
177 used in the CSS selector to namespace URIs. By default,
178 Beautiful Soup will pass in the prefixes it encountered while
179 parsing the document.
181 :param limit: After finding this number of results, stop looking.
183 :param flags: Flags to be passed into Soup Sieve's
184 `soupsieve.select() <https://facelessuser.github.io/soupsieve/api/#soupsieveselect>`_ method.
186 :param kwargs: Keyword arguments to be passed into Soup Sieve's
187 `soupsieve.select() <https://facelessuser.github.io/soupsieve/api/#soupsieveselect>`_ method.
188 """
189 if limit is None:
190 limit = 0
192 return self._rs(
193 self.api.select(
194 select, self.tag, self._ns(namespaces, select), limit, flags, **kwargs
195 )
196 )
198 def iselect(
199 self,
200 select: str,
201 namespaces: Optional[_NamespaceMapping] = None,
202 limit: int = 0,
203 flags: int = 0,
204 **kwargs: Any,
205 ) -> Iterator[element.Tag]:
206 """Perform a CSS selection operation on the current `element.Tag`.
208 This uses the Soup Sieve library. For more information, see
209 that library's documentation for the `soupsieve.iselect()
210 <https://facelessuser.github.io/soupsieve/api/#soupsieveiselect>`_
211 method. It is the same as select(), but it returns a generator
212 instead of a list.
214 :param selector: A string containing a CSS selector.
216 :param namespaces: A dictionary mapping namespace prefixes
217 used in the CSS selector to namespace URIs. By default,
218 Beautiful Soup will pass in the prefixes it encountered while
219 parsing the document.
221 :param limit: After finding this number of results, stop looking.
223 :param flags: Flags to be passed into Soup Sieve's
224 `soupsieve.iselect() <https://facelessuser.github.io/soupsieve/api/#soupsieveiselect>`_ method.
226 :param kwargs: Keyword arguments to be passed into Soup Sieve's
227 `soupsieve.iselect() <https://facelessuser.github.io/soupsieve/api/#soupsieveiselect>`_ method.
228 """
229 return self.api.iselect(
230 select, self.tag, self._ns(namespaces, select), limit, flags, **kwargs
231 )
233 def closest(
234 self,
235 select: str,
236 namespaces: Optional[_NamespaceMapping] = None,
237 flags: int = 0,
238 **kwargs: Any,
239 ) -> Optional[element.Tag]:
240 """Find the `element.Tag` closest to this one that matches the given selector.
242 This uses the Soup Sieve library. For more information, see
243 that library's documentation for the `soupsieve.closest()
244 <https://facelessuser.github.io/soupsieve/api/#soupsieveclosest>`_
245 method.
247 :param selector: A string containing a CSS selector.
249 :param namespaces: A dictionary mapping namespace prefixes
250 used in the CSS selector to namespace URIs. By default,
251 Beautiful Soup will pass in the prefixes it encountered while
252 parsing the document.
254 :param flags: Flags to be passed into Soup Sieve's
255 `soupsieve.closest() <https://facelessuser.github.io/soupsieve/api/#soupsieveclosest>`_ method.
257 :param kwargs: Keyword arguments to be passed into Soup Sieve's
258 `soupsieve.closest() <https://facelessuser.github.io/soupsieve/api/#soupsieveclosest>`_ method.
260 """
261 return self.api.closest(
262 select, self.tag, self._ns(namespaces, select), flags, **kwargs
263 )
265 def match(
266 self,
267 select: str,
268 namespaces: Optional[_NamespaceMapping] = None,
269 flags: int = 0,
270 **kwargs: Any,
271 ) -> bool:
272 """Check whether or not this `element.Tag` matches the given CSS selector.
274 This uses the Soup Sieve library. For more information, see
275 that library's documentation for the `soupsieve.match()
276 <https://facelessuser.github.io/soupsieve/api/#soupsievematch>`_
277 method.
279 :param: a CSS selector.
281 :param namespaces: A dictionary mapping namespace prefixes
282 used in the CSS selector to namespace URIs. By default,
283 Beautiful Soup will pass in the prefixes it encountered while
284 parsing the document.
286 :param flags: Flags to be passed into Soup Sieve's
287 `soupsieve.match()
288 <https://facelessuser.github.io/soupsieve/api/#soupsievematch>`_
289 method.
291 :param kwargs: Keyword arguments to be passed into SoupSieve's
292 `soupsieve.match()
293 <https://facelessuser.github.io/soupsieve/api/#soupsievematch>`_
294 method.
295 """
296 return cast(
297 bool,
298 self.api.match(
299 select, self.tag, self._ns(namespaces, select), flags, **kwargs
300 ),
301 )
303 def filter(
304 self,
305 select: str,
306 namespaces: Optional[_NamespaceMapping] = None,
307 flags: int = 0,
308 **kwargs: Any,
309 ) -> ResultSet[element.Tag]:
310 """Filter this `element.Tag`'s direct children based on the given CSS selector.
312 This uses the Soup Sieve library. It works the same way as
313 passing a `element.Tag` into that library's `soupsieve.filter()
314 <https://facelessuser.github.io/soupsieve/api/#soupsievefilter>`_
315 method. For more information, see the documentation for
316 `soupsieve.filter()
317 <https://facelessuser.github.io/soupsieve/api/#soupsievefilter>`_.
319 :param namespaces: A dictionary mapping namespace prefixes
320 used in the CSS selector to namespace URIs. By default,
321 Beautiful Soup will pass in the prefixes it encountered while
322 parsing the document.
324 :param flags: Flags to be passed into Soup Sieve's
325 `soupsieve.filter()
326 <https://facelessuser.github.io/soupsieve/api/#soupsievefilter>`_
327 method.
329 :param kwargs: Keyword arguments to be passed into SoupSieve's
330 `soupsieve.filter()
331 <https://facelessuser.github.io/soupsieve/api/#soupsievefilter>`_
332 method.
333 """
334 return self._rs(
335 self.api.filter(
336 select, self.tag, self._ns(namespaces, select), flags, **kwargs
337 )
338 )