Repository: sdispater/tomlkit

Issue:
Bug: tomlkit.value("test") returns True
Since tomlkit `0.8.0` all strings starting with lowercase letter `t` are converted to `True` and all strings starting with `f` are coverted to `False` if using the `tomlkit.value(...)` call.

E.g.: Install tomlkit `0.8.0` into a fresh venv and using `python3`:
```sh
❯ python3                   
Python 3.10.0 (default, Oct 22 2021, 13:24:07) [Clang 13.0.0 (clang-1300.0.29.3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import tomlkit
>>> tomlkit.value("test")
True
>>> tomlkit.value("fest")
False
>>> tomlkit.value("NoTest")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/robot/test/test123/lib/python3.10/site-packages/tomlkit/api.py", line 149, in value
    return Parser(raw)._parse_value()
  File "/Users/robot/test/test123/lib/python3.10/site-packages/tomlkit/parser.py", line 598, in _parse_value
    raise self.parse_error(UnexpectedCharError, c)
tomlkit.exceptions.UnexpectedCharError: Unexpected character: 'N' at line 1 col 0
```

**I would expect that all these strings throw an error like it is done in `0.7.2`.**

Discussion:

Patch:
diff --git a/tomlkit/api.py b/tomlkit/api.py
index 9a58be1..c4cc07f 100644
--- a/tomlkit/api.py
+++ b/tomlkit/api.py
@@ -8,6 +8,7 @@ from typing import Union
 
 from ._utils import parse_rfc3339
 from .container import Container
+from .exceptions import UnexpectedCharError
 from .items import AoT
 from .items import Array
 from .items import Bool
@@ -232,7 +233,11 @@ def value(raw: str) -> _Item:
     >>> value("[1, 2, 3]")
     [1, 2, 3]
     """
-    return Parser(raw)._parse_value()
+    parser = Parser(raw)
+    v = parser._parse_value()
+    if not parser.end():
+        raise parser.parse_error(UnexpectedCharError, char=parser._current)
+    return v
 
 
 def key_value(src: str) -> Tuple[Key, _Item]:
diff --git a/tomlkit/parser.py b/tomlkit/parser.py
index e3d0487..e1f64df 100644
--- a/tomlkit/parser.py
+++ b/tomlkit/parser.py
@@ -123,11 +123,11 @@ class Parser:
         """
         self._src.mark()
 
-    def parse_error(self, exception=ParseError, *args):
+    def parse_error(self, exception=ParseError, *args, **kwargs):
         """
         Creates a generic "parse error" at the current position.
         """
-        return self._src.parse_error(exception, *args)
+        return self._src.parse_error(exception, *args, **kwargs)
 
     def parse(self) -> TOMLDocument:
         body = TOMLDocument(True)
@@ -1082,7 +1082,7 @@ class Parser:
         with self._state(restore=True):
             buf = ""
             for _ in range(n):
-                if self._current not in " \t\n\r#,]}":
+                if self._current not in " \t\n\r#,]}" + self._src.EOF:
                     buf += self._current
                     self.inc()
                     continue
diff --git a/tomlkit/source.py b/tomlkit/source.py
index 06882c2..6f82d94 100644
--- a/tomlkit/source.py
+++ b/tomlkit/source.py
@@ -147,7 +147,7 @@ class Source(str):
 
         # failed to consume minimum number of characters
         if min > 0:
-            self.parse_error(UnexpectedCharError, self.current)
+            raise self.parse_error(UnexpectedCharError, self.current)
 
     def end(self) -> bool:
         """
@@ -162,14 +162,17 @@ class Source(str):
         self._marker = self._idx
 
     def parse_error(
-        self, exception: Type[ParseError] = ParseError, *args: Any
+        self,
+        exception: Type[ParseError] = ParseError,
+        *args: Any,
+        **kwargs: Any,
     ) -> ParseError:
         """
         Creates a generic "parse error" at the current position.
         """
         line, col = self._to_linecol()
 
-        return exception(line, col, *args)
+        return exception(line, col, *args, **kwargs)
 
     def _to_linecol(self) -> Tuple[int, int]:
         cur = 0


Test patch:
diff --git a/tests/test_api.py b/tests/test_api.py
index 4d24844..2cd360f 100644
--- a/tests/test_api.py
+++ b/tests/test_api.py
@@ -301,3 +301,76 @@ def test_add_dotted_key():
     table = tomlkit.table()
     table.add(tomlkit.key(["foo", "bar"]), 1)
     assert table.as_string() == "foo.bar = 1\n"
+
+
+@pytest.mark.parametrize(
+    ("raw", "expected"),
+    [
+        ("true", True),
+        ("false", False),
+    ],
+)
+def test_value_parses_boolean(raw, expected):
+    parsed = tomlkit.value(raw)
+    assert parsed == expected
+
+
+@pytest.mark.parametrize(
+    "raw", ["t", "f", "tru", "fals", "test", "friend", "truthy", "falsify"]
+)
+def test_value_rejects_values_looking_like_bool_at_start(raw):
+    """Reproduces https://github.com/sdispater/tomlkit/issues/165"""
+    with pytest.raises(tomlkit.exceptions.ParseError):
+        tomlkit.value(raw)
+
+
+@pytest.mark.parametrize(
+    "raw",
+    [
+        "truee",
+        "truely",
+        "true-thoughts",
+        "true_hip_hop",
+    ],
+)
+def test_value_rejects_values_having_true_prefix(raw):
+    """Values that have ``true`` or ``false`` as prefix but then have additional chars are rejected."""
+    with pytest.raises(tomlkit.exceptions.ParseError):
+        tomlkit.value(raw)
+
+
+@pytest.mark.parametrize(
+    "raw",
+    [
+        "falsee",
+        "falsely",
+        "false-ideas",
+        "false_prophet",
+    ],
+)
+def test_value_rejects_values_having_false_prefix(raw):
+    """Values that have ``true`` or ``false`` as prefix but then have additional chars are rejected."""
+    with pytest.raises(tomlkit.exceptions.ParseError):
+        tomlkit.value(raw)
+
+
+@pytest.mark.parametrize(
+    "raw",
+    [
+        '"foo"1.2',
+        "truefalse",
+        "1.0false",
+        "100true",
+        "truetrue",
+        "falsefalse",
+        "1.2.3.4",
+        "[][]",
+        "{a=[][]}[]",
+        "true[]",
+        "false{a=1}",
+    ],
+)
+def test_value_rejects_values_with_appendage(raw):
+    """Values that appear valid at the beginning but leave chars unparsed are rejected."""
+    with pytest.raises(tomlkit.exceptions.ParseError):
+        tomlkit.value(raw)