diff --git a/tests/test_api.py b/tests/test_api.py index b5f31bc3..dca95122 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -320,6 +320,50 @@ def test_key_value() -> None: assert isinstance(i, Integer) +@pytest.mark.parametrize( + "src", + ["foo = 12", "foo = 12\n", " foo = 12", "foo = 12 ", "foo = 12 # comment\n"], +) +def test_key_value_allows_trailing_whitespace_and_comment(src: str) -> None: + k, i = tomlkit.key_value(src) + + assert k.key == "foo" + assert i == 12 + + +@pytest.mark.parametrize( + "src", + [ + "foo = 12 junk", + "foo = 12 = 13", + "foo = 12]]]", + ], +) +def test_key_value_raises_on_trailing_chars(src: str) -> None: + # parse() rejects each of these; key_value() must agree. + with pytest.raises(UnexpectedCharError): + parse(src) + + with pytest.raises(UnexpectedCharError): + tomlkit.key_value(src) + + +def test_key_value_raises_on_a_second_pair() -> None: + # Two pairs are a valid document, but key_value() parses a single pair, + # so the second one is trailing input rather than a silently dropped value. + assert dict(parse("foo = 12\nbar = 13")) == {"foo": 12, "bar": 13} + + with pytest.raises(UnexpectedCharError): + tomlkit.key_value("foo = 12\nbar = 13") + + +def test_key_value_raises_on_invalid_example( + invalid_example: Callable[[str], str], +) -> None: + with pytest.raises(UnexpectedCharError): + tomlkit.key_value(invalid_example("key_value_with_trailing_chars")) + + def test_string() -> None: s = tomlkit.string('foo "') diff --git a/tomlkit/api.py b/tomlkit/api.py index b0f8cd6d..c7f528cc 100644 --- a/tomlkit/api.py +++ b/tomlkit/api.py @@ -290,12 +290,19 @@ def value(raw: str) -> _Item: def key_value(src: str) -> tuple[Key, _Item]: """Parse a key-value pair from a string. + Anything other than whitespace or a comment after the pair is an error, + as it is when the same text is given to :func:`parse`. + :Example: >>> key_value("foo = 1") (Key('foo'), 1) """ - return Parser(src)._parse_key_value() + parser = Parser(src) + k, v = parser._parse_key_value(parse_comment=True) + if not parser.end(): + raise parser.parse_error(UnexpectedCharError, char=parser._current) + return k, v def ws(src: str) -> Whitespace: