diff --git a/docs/index.rst b/docs/index.rst index 874977d..87a6a57 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -30,6 +30,13 @@ Install # local development, with uv uv sync +To read or write TOML front matter, install the optional dependency:: + + pip install 'python-frontmatter[toml]' + +Without it, parsing a document with TOML delimiters emits a warning and leaves +the document as content, without extracting its metadata. + Usage ------ @@ -152,4 +159,3 @@ Indices and tables * :ref:`genindex` * :ref:`modindex` * :ref:`search` - diff --git a/frontmatter/__init__.py b/frontmatter/__init__.py index 04f4029..e7e9842 100644 --- a/frontmatter/__init__.py +++ b/frontmatter/__init__.py @@ -6,6 +6,8 @@ import io import pathlib +import re +import warnings from os import PathLike from typing import TYPE_CHECKING, Iterable, TextIO @@ -80,6 +82,13 @@ def parse( # this will only run if a handler hasn't been set higher up handler = handler or detect_format(text, handlers) if handler is None: + if TOMLHandler is None and re.match(r"^\+{3,}\s*$", text, re.MULTILINE): + warnings.warn( + "TOML front matter detected, but TOML support is not installed. " + "Install 'python-frontmatter[toml]' to parse it.", + UserWarning, + stacklevel=2, + ) return metadata, text # split on the delimiters diff --git a/tests/test_missing_toml.py b/tests/test_missing_toml.py new file mode 100644 index 0000000..841ce44 --- /dev/null +++ b/tests/test_missing_toml.py @@ -0,0 +1,35 @@ +"""Exercise optional TOML support in an interpreter without the dependency.""" + +import subprocess +import sys +import textwrap + + +def test_missing_toml_warning(): + result = subprocess.run( + [sys.executable, "-c", textwrap.dedent(''' + import sys + import warnings + sys.modules["toml"] = None + import frontmatter + + text = '+++\\ntitle = "Hello"\\n+++\\nBody' + with warnings.catch_warnings(record=True) as caught: + warnings.simplefilter("always") + post = frontmatter.loads(text) + assert len(caught) == 1 + assert "python-frontmatter[toml]" in str(caught[0].message) + assert post.content == text + assert post.metadata == {} + + with warnings.catch_warnings(record=True) as caught: + warnings.simplefilter("always") + assert frontmatter.loads('---\\ntitle: Hello\\n---\\nBody')["title"] == "Hello" + assert frontmatter.loads('Body\\n+++').content == 'Body\\n+++' + assert frontmatter.loads('++++ text').content == '++++ text' + assert not caught + ''')], + capture_output=True, + text=True, + ) + assert result.returncode == 0, result.stderr