Repository: sdispater/tomlkit

Issue:
Newline in keys should be escaped
Hi I was looking at the tomlkit behaviour with newlines in keys and found a bug:
``` python
>>> import tomlkit
>>> tomlkit.__version__
'0.5.11'
>>> print(tomlkit.dumps({'a\nb': 'a\nb'}))
"a
b" = "a\nb"
```

The toml spec says that the key, equal sign, and value must be on the same line (the value can span multiple lines if it is a multiline string but it must start on the same line as the other parts).  ( https://toml.io/en/v1.0.0#keyvalue-pair )

I asked for clarification of what should happen with newlines in key values on the toml lang github and the consensus was that the escaped form of a newline should be used instead of the actual newline: https://github.com/toml-lang/toml/issues/826

Thus the desired behaviour would be for tomlkit.dumps do this:

``` python
>>> print(tomlkit.dumps({'a\nb': 'a\nb'}))
"a\nb" = "a\nb"
```

Discussion:

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d4d3b1a..91ab737 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,7 @@
 
 ### Fixed
 
+- Escape characters in double quoted key string. ([#136](https://github.com/sdispater/tomlkit/issues/#136))
 - Fix the invalid dumping output of multiline array when it is empty. ([#139](https://github.com/sdispater/tomlkit/issues/#139))
 - Fix a bug that tomlkit accepts an invalid table with missing `=`. ([#141](https://github.com/sdispater/tomlkit/issues/141))
 - Fix the invalid dumping output when the key is empty. ([#143](https://github.com/sdispater/tomlkit/issues/143))
diff --git a/tomlkit/_utils.py b/tomlkit/_utils.py
index 570467a..f3fa49f 100644
--- a/tomlkit/_utils.py
+++ b/tomlkit/_utils.py
@@ -135,17 +135,3 @@ def merge_dicts(d1: dict, d2: dict) -> dict:
             merge_dicts(d1[k], v)
         else:
             d1[k] = d2[k]
-
-
-def escape_quotes(s: str, quote: str) -> str:
-    escaped = False
-    result = ""
-    for c in s:
-        if escaped:
-            escaped = False
-        elif c == "\\":
-            escaped = True
-        elif c == quote:
-            result += "\\"
-        result += c
-    return result
diff --git a/tomlkit/items.py b/tomlkit/items.py
index 460feee..50ebfa3 100644
--- a/tomlkit/items.py
+++ b/tomlkit/items.py
@@ -22,7 +22,6 @@ from typing import overload
 
 from ._compat import PY38
 from ._compat import decode
-from ._utils import escape_quotes
 from ._utils import escape_string
 from .toml_char import TOMLChar
 
@@ -291,7 +290,8 @@ class SingleKey(Key):
         self.sep = sep
         self.key = k
         if original is None:
-            original = t.value + escape_quotes(k, t.value) + t.value
+            key_str = escape_string(k) if t == KeyType.Basic else k
+            original = f"{t.value}{key_str}{t.value}"
 
         self._original = original
         self._keys = [self]


Test patch:
diff --git a/tests/test_write.py b/tests/test_write.py
index b68de6e..58752d9 100644
--- a/tests/test_write.py
+++ b/tests/test_write.py
@@ -10,3 +10,10 @@ def test_write_backslash():
 
     assert expected == dumps(d)
     assert loads(dumps(d))["foo"] == "\\e\u25E6\r"
+
+
+def test_escape_special_characters_in_key():
+    d = {"foo\nbar": "baz"}
+    expected = '"foo\\nbar" = "baz"\n'
+    assert expected == dumps(d)
+    assert loads(dumps(d))["foo\nbar"] == "baz"