Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,8 @@ strip_document
Controls whether leading and/or trailing separation newlines are removed from
the final converted document. Supported values are ``LSTRIP`` (leading),
``RSTRIP`` (trailing), ``STRIP`` (both), and ``None`` (neither). Newlines
within the document are unaffected.
within the document are unaffected. This also applies when ``convert_soup()``
receives an individual BeautifulSoup ``Tag`` rather than a complete soup.
Defaults to ``STRIP``.

strip_pre
Expand Down
9 changes: 8 additions & 1 deletion markdownify/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,14 @@ def convert(self, html):
return self.convert_soup(soup)

def convert_soup(self, soup):
return self.process_tag(soup, parent_tags=set())
text = self.process_tag(soup, parent_tags=set())
# A supplied Tag is also a complete conversion input. BeautifulSoup's
# document node already runs this finalizer during process_tag().
if soup.name != '[document]':
convert_fn = self.get_conv_fn_cached('[document]')
if convert_fn is not None:
text = convert_fn(soup, text, parent_tags={'[document]'})
return text

def process_element(self, node, parent_tags=None):
if isinstance(node, NavigableString):
Expand Down
46 changes: 46 additions & 0 deletions tests/test_custom_converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,49 @@ def test_soup():
html = '<b>test</b>'
soup = BeautifulSoup(html, 'html.parser')
assert MarkdownConverter().convert_soup(soup) == '**test**'


def test_tag_document_stripping():
from markdownify import LSTRIP, RSTRIP, STRIP

html = '<html><body><div><p>hello</p><p>world</p></div></body></html>'
for mode, expected in [(LSTRIP, 'hello\n\nworld\n\n'),
(RSTRIP, '\n\nhello\n\nworld'),
(STRIP, 'hello\n\nworld'),
(None, '\n\nhello\n\nworld\n\n')]:
for name in ['html', 'body', 'div']:
soup = BeautifulSoup(html, 'html.parser')
tag = soup.find(name)
before = str(soup)
assert MarkdownConverter(strip_document=mode).convert_soup(tag) == expected
assert str(soup) == before
assert tag.parent is not None


def test_tag_document_invalid_strip_mode():
import pytest

soup = BeautifulSoup('<p>hello</p>', 'html.parser')
with pytest.raises(ValueError, match='Invalid value for strip_document'):
MarkdownConverter(strip_document='invalid').convert_soup(soup.p)


def test_document_converter_called_once():
class CountingConverter(MarkdownConverter):
calls = 0

def convert__document_(self, el, text, parent_tags):
self.calls += 1
return super().convert__document_(el, text, parent_tags)

soup = BeautifulSoup('<div><p>hello</p></div>', 'html.parser')
for root in [soup, soup.div, soup.p]:
converter = CountingConverter()
assert converter.convert_soup(root) == 'hello'
assert converter.calls == 1


def test_tag_document_converter_exclusion():
soup = BeautifulSoup('<p>hello</p>', 'html.parser')
for root in [soup, soup.p]:
assert MarkdownConverter(strip=['[document]']).convert_soup(root) == '\n\nhello\n\n'