Przeglądaj źródła

- Fixed a bug introduced in 3.0a7 where parsing a string of key/value
pairs failed on Python 2.3 due to use of regular expression syntax
introduced in Python 2.4.

Mike Naberezny 15 lat temu
rodzic
commit
b1d2fb8aad
3 zmienionych plików z 32 dodań i 20 usunięć
  1. 4 0
      CHANGES.txt
  2. 11 19
      src/supervisor/datatypes.py
  3. 17 1
      src/supervisor/tests/test_datatypes.py

+ 4 - 0
CHANGES.txt

@@ -9,6 +9,10 @@ Next Release
   - Fixed a bug introduced in 3.0a7 where setup.py would not detect the
     Python version correctly.  Patch by Daniele Paolella.
 
+  - Fixed a bug introduced in 3.0a7 where parsing a string of key/value
+    pairs failed on Python 2.3 due to use of regular expression syntax
+    introduced in Python 2.4.
+
   - Removed the test suite for the ``memmon`` console script, which was 
     moved to the Superlance package in 3.0a7.
 

+ 11 - 19
src/supervisor/datatypes.py

@@ -16,6 +16,7 @@ import os
 import re
 import sys
 import socket
+import shlex
 from supervisor.loggers import getLevelNumByDescription
 
 # I dont know why we bother, this doesn't run on Windows, but just
@@ -79,30 +80,21 @@ def list_of_exitcodes(arg):
     except:
         raise ValueError("not a valid list of exit codes: " + repr(arg))
 
-# Regular expression to match key=value, key='value' or key="value" strings
-# Quoted values can allow commas, unquoted will not
-DICT_OF_KVP_REGEXP = re.compile(r"[ \t]*(?P<key>\w+)[ \t]*=[ \t]"
-                                r"*(?P<quote>['\"])?"
-                                r"(?P<value>.*?)(?(quote)['\"])[,$]")
-
 def dict_of_key_value_pairs(arg):
     """ parse KEY=val,KEY2=val2 into {'KEY':'val', 'KEY2':'val2'}
         Quotes can be used to allow commas in the value
     """
-    D = {}
-
-    if len(arg):
-        # Work-around non-greedy value match won't let the regexp parse
-        # the last key=value pair if value is not quoted
-        if arg[-1] != ',':
-            arg += ','
-
-        for key, quote, value in DICT_OF_KVP_REGEXP.findall(arg):
-            D[key] = value.strip()
-
-        if D == {}:
-            raise ValueError("not a list of key/value pairs: " + repr(arg))
+    tokens = list(shlex.shlex(arg))
+    tokens_len = len(tokens)
 
+    D = {}
+    i = 0
+    while i in range(0, tokens_len):   
+        k_eq_v = tokens[i:i+3]
+        if len(k_eq_v) != 3:
+            raise ValueError, "Unexpected end of key/value pairs"
+        D[k_eq_v[0]] = k_eq_v[2].strip('\'"')
+        i += 4
     return D
 
 class Automatic:

+ 17 - 1
src/supervisor/tests/test_datatypes.py

@@ -100,6 +100,11 @@ class DatatypesTest(unittest.TestCase):
         expected = {'foo': 'bar', 'baz': 'qux'}
         self.assertEqual(actual, expected)
 
+    def test_dict_of_key_value_pairs_returns_dict_even_if_newlines(self):
+        actual = datatypes.dict_of_key_value_pairs('foo\n=\nbar\n,\nbaz\n=\nqux')
+        expected = {'foo': 'bar', 'baz': 'qux'}
+        self.assertEqual(actual, expected)
+
     def test_dict_of_key_value_pairs_handles_commas_inside_apostrophes(self):
         actual = datatypes.dict_of_key_value_pairs("foo='bar,baz',baz='q,ux'")
         expected = {'foo': 'bar,baz', 'baz': 'q,ux'}
@@ -110,9 +115,20 @@ class DatatypesTest(unittest.TestCase):
         expected = {'foo': 'bar,baz', 'baz': 'q,ux'}
         self.assertEqual(actual, expected)
 
-    def test_dict_of_key_value_pairs_raises_value_error_on_weird_input(self):
+    def test_dict_of_key_value_pairs_allows_trailing_comma(self):
+        actual = datatypes.dict_of_key_value_pairs('foo=bar,')
+        expected = {'foo': 'bar'}
+        self.assertEqual(actual, expected)
+
+    def test_dict_of_key_value_pairs_raises_value_error_on_too_short(self):
         self.assertRaises(ValueError, 
                           datatypes.dict_of_key_value_pairs, 'foo')
+        self.assertRaises(ValueError, 
+                          datatypes.dict_of_key_value_pairs, 'foo=')
+        self.assertRaises(ValueError, 
+                          datatypes.dict_of_key_value_pairs, 'foo=bar,baz')
+        self.assertRaises(ValueError, 
+                          datatypes.dict_of_key_value_pairs, 'foo=bar,baz=')
 
     def test_logfile_name_returns_none_for_none_values(self):
         for thing in datatypes.LOGFILE_NONES: