Skip to content

Commit

Permalink
Add select_one api function (#46)
Browse files Browse the repository at this point in the history
  • Loading branch information
facelessuser authored Dec 27, 2018
1 parent 8954ee4 commit 43e365b
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 1 deletion.
4 changes: 4 additions & 0 deletions docs/src/markdown/about/changelog.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
24 changes: 24 additions & 0 deletions docs/src/markdown/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
<p class="a">Cat</p>
```

## `soupsieve.select()`

```py3
Expand Down Expand Up @@ -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."""

Expand Down
10 changes: 9 additions & 1 deletion docs/src/markdown/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
<p class="a">Cat</p>
```

To select all tags:

```pycon3
>>> import soupsieve as sv
Expand Down
6 changes: 6 additions & 0 deletions soupsieve/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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."""

Expand Down
6 changes: 6 additions & 0 deletions soupsieve/css_match.py
Original file line number Diff line number Diff line change
Expand Up @@ -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."""

Expand Down
13 changes: 13 additions & 0 deletions tests/test_soupsieve.py
Original file line number Diff line number Diff line change
Expand Up @@ -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'])
Expand Down

0 comments on commit 43e365b

Please sign in to comment.