Преглед на файлове

Values for subprocess environment variables specified with environment=
in supervisord.conf can now be optionally quoted, allowing them to
contain commas. Patch by Tim Godfrey.

Mike Naberezny преди 16 години
родител
ревизия
52253382cc
променени са 3 файла, в които са добавени 41 реда и са изтрити 11 реда
  1. 4 0
      CHANGES.txt
  2. 23 11
      src/supervisor/datatypes.py
  3. 14 0
      src/supervisor/tests/test_datatypes.py

+ 4 - 0
CHANGES.txt

@@ -1,5 +1,9 @@
 Next Release
 
+  - Values for subprocess environment variables specified with environment=
+    in supervisord.conf can now be optionally quoted, allowing them to 
+    contain commas.  Patch by Tim Godfrey.
+
   - Added a new event type, REMOTE_COMMUNICATION, that is emitted by a new
     RPC method, supervisor.sendRemoteCommEvent().
 

+ 23 - 11
src/supervisor/datatypes.py

@@ -13,6 +13,7 @@
 ##############################################################################
 
 import os
+import re
 import sys
 import socket
 from supervisor.loggers import getLevelNumByDescription
@@ -78,19 +79,30 @@ 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'} """
+    """ parse KEY=val,KEY2=val2 into {'KEY':'val', 'KEY2':'val2'}
+        Quotes can be used to allow commas in the value
+    """
     D = {}
-    try:
-        pairs = filter(None, arg.split(','))
-        for pair in pairs:
-            try:
-                k, v = pair.split('=', 1)
-            except ValueError:
-                raise ValueError('Unknown key/value pair %s' % pair)
-            D[k.strip()] = v.strip()
-    except:
-        raise ValueError("not a list of key/value pairs: " + repr(arg))        
+
+    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))
+
     return D
 
 class Automatic:

+ 14 - 0
src/supervisor/tests/test_datatypes.py

@@ -99,6 +99,20 @@ class DatatypesTest(unittest.TestCase):
         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'}
+        self.assertEqual(actual, expected)
+
+    def test_dict_of_key_value_pairs_handles_commas_inside_quotes(self):
+        actual = datatypes.dict_of_key_value_pairs('foo="bar,baz",baz="q,ux"')
+        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):
+        self.assertRaises(ValueError, 
+                          datatypes.dict_of_key_value_pairs, 'foo')
+
     def test_logfile_name_returns_none_for_none_values(self):
         for thing in datatypes.LOGFILE_NONES:
             actual = datatypes.logfile_name(thing)