diff --git a/docs/src/markdown/about/changelog.md b/docs/src/markdown/about/changelog.md index 431b37d0..fe398a92 100644 --- a/docs/src/markdown/about/changelog.md +++ b/docs/src/markdown/about/changelog.md @@ -1,5 +1,9 @@ # Changelog +## 1.4.1 + +- **NEW**: Add `select_one` method like Beautiful Soup has. + ## 1.4.0 - **NEW**: Throw `NotImplementedError` for at-rules: `@page`, etc. diff --git a/docs/src/markdown/api.md b/docs/src/markdown/api.md index c72b648e..46cea96a 100644 --- a/docs/src/markdown/api.md +++ b/docs/src/markdown/api.md @@ -16,10 +16,31 @@ Soup Sieve will detect the document type being used from the Beautiful Soup obje - While attribute values are generally treated as case sensitive, HTML5, XHTML, and HTML treat the `type` attribute special. The `type` attribute's value is always case insensitive. This is generally how most browsers treat `type`. If you need `type` to be sensitive, you can use the `s` flag: `#!css [type="submit" s]`. +As far as the API is concerned, Soup Sieve mimics Beautiful Soup's original API at the time of writing this, which is why the names `select` and `select_one` are used. As of today, Beautiful Soup has agreed to include Soup Sieve as the official select library which is slated for the 4.7.0 release. + +Soup Sieve will always be available as an external API as well for more controlled tag selection if needed. + ## Flags Early in development, flags were used to specify document type, but as of 1.0.0, there are no flags used at this time, but the parameter is provided for potential future use. +## `soupsieve.select_one()` + +```py3 +def select(select, tag, namespaces=None, flags=0): + """Select the specified tags.""" +``` + +`select_one` will return the first tag under the given tag that matches the given CSS selectors provided, or it will return `None` if a suitable tag was not found. + +`select_one` accepts a CSS selector string, a `Tag`/`BeautifulSoup` object, an optional [namespace](#namespaces) dictionary, and `flags`. + +```pycon3 +>>> import soupsieve as sv +>>> sv.select_one('p:is(.a, .b, .c)', soup) +
Cat
+``` + ## `soupsieve.select()` ```py3 @@ -126,6 +147,9 @@ class SoupSieve: def icomments(self, tag, limit=0): """Iterate comments only.""" + def select_one(self, tag): + """Select a single tag.""" + def select(self, tag, limit=0): """Select the specified tags.""" diff --git a/docs/src/markdown/index.md b/docs/src/markdown/index.md index 20739a05..f669b41d 100644 --- a/docs/src/markdown/index.md +++ b/docs/src/markdown/index.md @@ -56,7 +56,15 @@ To use Soup Sieve, you must create a `BeautifulSoup` object: >>> soup = bs4.BeautifulSoup(text, 'html5lib') ``` -Then you can begin to use Soup Sieve to select: +Then you can begin to use Soup Sieve to select a single tag: + +```pycon3 +>>> import soupsieve as sv +>>> sv.select_one('p:is(.a, .b, .c)', soup) +Cat
+``` + +To select all tags: ```pycon3 >>> import soupsieve as sv diff --git a/soupsieve/__init__.py b/soupsieve/__init__.py index 15b4c597..b5f594d7 100644 --- a/soupsieve/__init__.py +++ b/soupsieve/__init__.py @@ -89,6 +89,12 @@ def icomments(tag, limit=0, flags=0): yield comment +def select_one(select, tag, namespaces=None, flags=0): + """Select a single tag.""" + + return compile(select, namespaces, flags).select_one(tag) + + def select(select, tag, namespaces=None, limit=0, flags=0): """Select the specified tags.""" diff --git a/soupsieve/css_match.py b/soupsieve/css_match.py index 2b37cf71..9415b2fb 100644 --- a/soupsieve/css_match.py +++ b/soupsieve/css_match.py @@ -764,6 +764,12 @@ def icomments(self, tag, limit=0): for comment in self._sieve(tag, capture=False, comments=True, limit=limit): yield comment + def select_one(self, tag): + """Select a single tag.""" + + tags = self.select(tag, limit=1) + return tags[0] if tags else None + def select(self, tag, limit=0): """Select the specified tags.""" diff --git a/tests/test_soupsieve.py b/tests/test_soupsieve.py index 091a9b59..a8d0c5cf 100644 --- a/tests/test_soupsieve.py +++ b/tests/test_soupsieve.py @@ -67,6 +67,19 @@ def test_select(self): self.assertEqual(sorted(['5', 'some-id']), sorted(ids)) + ids = [] + for el in sv.select('span[id]', soup, limit=1): + ids.append(el.attrs['id']) + + self.assertEqual(sorted(['5']), sorted(ids)) + + self.assertEqual( + sv.select('span[id]', soup, limit=1)[0].attrs['id'], + sv.select_one('span[id]', soup).attrs['id'] + ) + + self.assertEqual(None, sv.select_one('h1', soup)) + ids = [] for el in sv.iselect('span[id]', soup): ids.append(el.attrs['id'])