From aca4d78f80fa29a54388f5ed6e6094e144421bfc Mon Sep 17 00:00:00 2001 From: eeshsaxena Date: Wed, 12 Aug 2026 14:23:38 +0530 Subject: [PATCH] Don't crash on front matter with non-string mapping keys loads did Post(content, handler, **metadata), which raises 'keywords must be strings' when the parsed metadata has non-string keys. Integers, booleans and dates are all valid YAML/TOML mapping keys, so a document like '---\n1: one\n---' blew up even though the front matter is valid. Assign the metadata dict to the Post directly instead of splatting it. --- frontmatter/__init__.py | 8 +++++++- tests/unit_test.py | 8 ++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/frontmatter/__init__.py b/frontmatter/__init__.py index 04f4029..66c9b45 100644 --- a/frontmatter/__init__.py +++ b/frontmatter/__init__.py @@ -191,7 +191,13 @@ def loads( text = u(text, encoding) handler = handler or detect_format(text, handlers) metadata, content = parse(text, encoding, handler, **defaults) - return Post(content, handler, **metadata) + # Assign the metadata dict directly rather than splatting it as keyword + # arguments: YAML/TOML mappings allow non-string keys (ints, booleans, + # dates), and Post(content, handler, **metadata) raises "keywords must be + # strings" on those, even though the front matter itself is valid. + post = Post(content, handler) + post.metadata = metadata + return post def dump( diff --git a/tests/unit_test.py b/tests/unit_test.py index 188fb4c..d58488e 100644 --- a/tests/unit_test.py +++ b/tests/unit_test.py @@ -61,6 +61,14 @@ def test_check_empty_frontmatter(self): self.assertEqual(ret, True) + def test_non_string_metadata_keys(self): + "YAML allows non-string mapping keys (e.g. ints); loads must not choke on them." + post = frontmatter.loads("---\n1: one\n2: two\n---\nbody") + self.assertEqual(post.metadata, {1: "one", 2: "two"}) + self.assertEqual(post.content, "body") + # and the post still round-trips through dumps/loads + self.assertEqual(frontmatter.loads(frontmatter.dumps(post)).metadata, post.metadata) + def test_no_frontmatter(self): "This is not a zen exercise." post = frontmatter.load("tests/empty/no-frontmatter.txt")