Przeglądaj źródła

Fix formatting/parsing.

Jelmer Vernooij 13 lat temu
rodzic
commit
0ffdc4d601
2 zmienionych plików z 78 dodań i 7 usunięć
  1. 40 6
      dulwich/config.py
  2. 38 1
      dulwich/tests/test_config.py

+ 40 - 6
dulwich/config.py

@@ -95,6 +95,24 @@ class ConfigDict(Config):
         section.setdefault(subsection_name, {})[variable] = value
 
 
+def _format_string(value):
+    if (value.startswith(" ") or
+        value.startswith("\t") or
+        value.endswith(" ") or
+        value.endswith("\t")):
+        return '"%s"' % _escape_value(value)
+    return _escape_value(value)
+
+
+def _parse_string(value):
+    value = value.strip()
+    if value.startswith("\""):
+        if not value.endswith("\""):
+            raise ValueError("value starts with quote but lacks end quote")
+        return _unescape_value(value[1:-1])
+    return _unescape_value(value)
+
+
 def _unescape_value(value):
     """Unescape a value."""
     def unescape(c):
@@ -141,15 +159,31 @@ class ConfigFile(ConfigDict):
                     if section is None:
                         raise ValueError("setting %r without section" % line)
                     setting = setting.strip()
-                    value = value.strip()
-                    ret._values[section[0]][section[1]][setting] = ""
+                    if value.endswith("\\\n"):
+                        value = value[:-2]
+                        continuation = True
+                    else:
+                        continuation = True
+                    value = _parse_string(value)
+                    ret._values[section[0]][section[1]][setting] = value
+                    if not continuation:
+                        setting = None
                 else:
                     setting = line.strip()
-                    value = ""
-            if setting is not None:
-                if section is None:
-                    raise ValueError("setting %r without section" % line)
+                    if section is None:
+                        raise ValueError("setting %r without section" % line)
+                    ret._values[section[0]][section[1]][setting] = ""
+                    setting = None
+            else:
+                if line.endswith("\\\n"):
+                    line = line[:-2]
+                    continuation = True
+                else:
+                    continuation = True
+                value = _parse_string(line)
                 ret._values[section[0]][section[1]][setting] += value
+                if not continuation:
+                    setting = None
         return ret
 
     @classmethod

+ 38 - 1
dulwich/tests/test_config.py

@@ -23,7 +23,9 @@ from dulwich.config import (
     ConfigDict,
     ConfigFile,
     StackedConfig,
+    _format_string,
     _escape_value,
+    _parse_string,
     _unescape_value,
     )
 from dulwich.tests import TestCase
@@ -49,6 +51,19 @@ class ConfigFileTests(TestCase):
         self.assertEquals("bar", cf.get("core.foo"))
         self.assertEquals("bar", cf.get("core.foo.foo"))
 
+    def test_from_file_with_quotes(self):
+        cf = self.from_file(
+            "[core]\n"
+            'foo = " bar"\n')
+        self.assertEquals(" bar", cf.get("core.foo"))
+
+    def test_from_file_with_interrupted_line(self):
+        cf = self.from_file(
+            "[core]\n"
+            'foo = bar\\\n'
+            ' la\n')
+        self.assertEquals("barla", cf.get("core.foo"))
+
     def test_from_file_subsection(self):
         cf = self.from_file("[branch \"foo\"]\nfoo = bar\n")
         self.assertEquals("bar", cf.get("branch.foo.foo"))
@@ -106,7 +121,7 @@ class UnescapeTests(TestCase):
         self.assertEquals("\"foo\"", _unescape_value("\\\"foo\\\""))
 
 
-class EscapeTests(TestCase):
+class EscapeValueTests(TestCase):
 
     def test_nothing(self):
         self.assertEquals("foo", _escape_value("foo"))
@@ -116,3 +131,25 @@ class EscapeTests(TestCase):
 
     def test_newline(self):
         self.assertEquals("foo\\n", _escape_value("foo\n"))
+
+
+class FormatStringTests(TestCase):
+
+    def test_quoted(self):
+        self.assertEquals('" foo"', _format_string(" foo"))
+        self.assertEquals('"\\tfoo"', _format_string("\tfoo"))
+
+    def test_not_quoted(self):
+        self.assertEquals('foo', _format_string("foo"))
+        self.assertEquals('foo bar', _format_string("foo bar"))
+
+
+class ParseStringTests(TestCase):
+
+    def test_quoted(self):
+        self.assertEquals(' foo', _parse_string('" foo"'))
+        self.assertEquals('\tfoo', _parse_string('"\\tfoo"'))
+
+    def test_not_quoted(self):
+        self.assertEquals('foo', _parse_string("foo"))
+        self.assertEquals('foo bar', _parse_string("foo bar"))