Переглянути джерело

- Use rich comparison methods rather than __cmp__ to sort process
configs and process group configs to better straddle Python
versions. (thanks to Jonathan Riboux for identifying the problem
and supplying an initial patch).

- Fixed test_supervisorctl.test_maintail_dashf test for Python 2.7.
(thanks to Jonathan Riboux for identifying the problem and
supplying an initial patch).

- Fixed the way that supervisor.datatypes.url computes a "good" URL
for compatibility with Python 2.7 and Python >= 2.6.5. URLs with
bogus "schemes://" will now be accepted as a version-straddling
compromise (before they were rejected before supervisor would
start). (thanks to Jonathan Riboux for identifying the problem
and supplying an initial patch).

Chris McDonough 15 роки тому
батько
коміт
0c5547b03c

+ 16 - 0
CHANGES.txt

@@ -1,5 +1,21 @@
 Next release
 Next release
 
 
+  - Use rich comparison methods rather than __cmp__ to sort process
+    configs and process group configs to better straddle Python
+    versions.  (thanks to Jonathan Riboux for identifying the problem
+    and supplying an initial patch).
+
+  - Fixed test_supervisorctl.test_maintail_dashf test for Python 2.7.
+    (thanks to Jonathan Riboux for identifying the problem and
+    supplying an initial patch).
+
+  - Fixed the way that supervisor.datatypes.url computes a "good" URL
+    for compatibility with Python 2.7 and Python >= 2.6.5.  URLs with
+    bogus "schemes://" will now be accepted as a version-straddling
+    compromise (before they were rejected before supervisor would
+    start).  (thanks to Jonathan Riboux for identifying the problem
+    and supplying an initial patch).
+
   - Add a ``-v`` / ``--version`` option to supervisord: Print the
   - Add a ``-v`` / ``--version`` option to supervisord: Print the
     supervisord version number out to stdout and exit.  (Roger Hoover)
     supervisord version number out to stdout and exit.  (Roger Hoover)
 
 

+ 2 - 0
setup.cfg

@@ -2,6 +2,8 @@
 zip_ok = false
 zip_ok = false
 
 
 [nosetests]
 [nosetests]
+exclude=medusa
+where=src/supervisor
 nocapture=1
 nocapture=1
 cover-package=supervisor
 cover-package=supervisor
 with-coverage=1
 with-coverage=1

+ 5 - 4
src/supervisor/datatypes.py

@@ -401,10 +401,11 @@ byte_size = SuffixMultiplier({'kb': 1024,
 
 
 def url(value):
 def url(value):
     import urlparse
     import urlparse
-    scheme, netloc, path, params, query, fragment = urlparse.urlparse(value)
-    if scheme and netloc:
-        return value     
-    if scheme == 'unix' and path.startswith('//') and len(path) > 2:
+    # earlier Python 2.6 urlparse (2.6.4 and under) can't parse unix:// URLs,
+    # later ones can but we need to straddle
+    uri = value.replace('unix://', 'http://', 1).strip()
+    scheme, netloc, path, params, query, fragment = urlparse.urlparse(uri)
+    if scheme and (netloc or path):
         return value
         return value
     raise ValueError("value %s is not a URL" % value)
     raise ValueError("value %s is not a URL" % value)
 
 

+ 37 - 8
src/supervisor/options.py

@@ -694,7 +694,6 @@ class ServerOptions(Options):
                 FastCGIGroupConfig(self, program_name, priority, processes,
                 FastCGIGroupConfig(self, program_name, priority, processes,
                                    socket_config)
                                    socket_config)
                 )
                 )
-        
 
 
         groups.sort()
         groups.sort()
         return groups
         return groups
@@ -1508,14 +1507,38 @@ class UnhosedConfigParser(ConfigParser.RawConfigParser):
             else:
             else:
                 return default
                 return default
 
 
-class Config:
-    def __cmp__(self, other):
-        return cmp(self.priority, other.priority)
+class Config(object):
+    def __ne__(self, other):
+        return not self.__eq__(other)
+
+    def __lt__(self, other):
+        if self.priority == other.priority:
+            return self.name < other.name
+            
+        return self.priority < other.priority
+
+    def __le__(self, other):
+        if self.priority == other.priority:
+            return self.name <= other.name
+            
+        return self.priority <= other.priority
+
+    def __gt__(self, other):
+        if self.priority == other.priority:
+            return self.name > other.name
+            
+        return self.priority > other.priority
+
+    def __ge__(self, other):
+        if self.priority == other.priority:
+            return self.name >= other.name
+            
+        return self.priority >= other.priority
 
 
     def __repr__(self):
     def __repr__(self):
         return '<%s instance at %s named %s>' % (self.__class__, id(self),
         return '<%s instance at %s named %s>' % (self.__class__, id(self),
                                                  self.name)
                                                  self.name)
-    
+
 class ProcessConfig(Config):
 class ProcessConfig(Config):
     req_param_names = [
     req_param_names = [
         'name', 'uid', 'command', 'directory', 'umask', 'priority',
         'name', 'uid', 'command', 'directory', 'umask', 'priority',
@@ -1642,9 +1665,6 @@ class ProcessGroupConfig(Config):
 
 
         return True
         return True
 
 
-    def __ne__(self, other):
-        return not self.__eq__(other)
-
     def after_setuid(self):
     def after_setuid(self):
         for config in self.process_configs:
         for config in self.process_configs:
             config.create_autochildlogs()
             config.create_autochildlogs()
@@ -1664,6 +1684,15 @@ class EventListenerPoolConfig(Config):
         self.pool_events = pool_events
         self.pool_events = pool_events
         self.result_handler = result_handler
         self.result_handler = result_handler
 
 
+    def __eq__(self, other):
+        if not isinstance(other, EventListenerPoolConfig):
+            return False
+        
+        if (self.name == other.name) and (self.priority == other.priority):
+            return True
+
+        return False
+            
     def after_setuid(self):
     def after_setuid(self):
         for config in self.process_configs:
         for config in self.process_configs:
             config.create_autochildlogs()
             config.create_autochildlogs()

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

@@ -198,10 +198,6 @@ class DatatypesTest(unittest.TestCase):
         bad_url = "unix://"
         bad_url = "unix://"
         self.assertRaises(ValueError, datatypes.url, bad_url)   
         self.assertRaises(ValueError, datatypes.url, bad_url)   
     
     
-    def test_url_rejects_urlparse_unrecognized_scheme_with_path(self):
-        bad_url = "bad://path"
-        self.assertRaises(ValueError, datatypes.url, bad_url)
-
 class InetStreamSocketConfigTests(unittest.TestCase):
 class InetStreamSocketConfigTests(unittest.TestCase):
     def _getTargetClass(self):
     def _getTargetClass(self):
         return datatypes.InetStreamSocketConfig
         return datatypes.InetStreamSocketConfig

+ 35 - 15
src/supervisor/tests/test_options.py

@@ -403,8 +403,13 @@ class ServerOptionsTests(unittest.TestCase):
         instance.realize(args=[])
         instance.realize(args=[])
 
 
         section = instance.configroot.supervisord
         section = instance.configroot.supervisord
+
         self.assertEqual(len(section.process_group_configs), 2)
         self.assertEqual(len(section.process_group_configs), 2)
+
         cat = section.process_group_configs[0]
         cat = section.process_group_configs[0]
+        self.assertEqual(len(cat.process_configs), 1)
+
+        cat = section.process_group_configs[1]
         self.assertEqual(len(cat.process_configs), 2)
         self.assertEqual(len(cat.process_configs), 2)
         self.assertTrue(section.process_group_configs is
         self.assertTrue(section.process_group_configs is
                         instance.process_group_configs)
                         instance.process_group_configs)
@@ -426,14 +431,10 @@ class ServerOptionsTests(unittest.TestCase):
         instance.process_config_file()
         instance.process_config_file()
 
 
         section = instance.configroot.supervisord
         section = instance.configroot.supervisord
+
         self.assertEqual(len(section.process_group_configs), 2)
         self.assertEqual(len(section.process_group_configs), 2)
-        cat = section.process_group_configs[0]
-        self.assertEqual(len(cat.process_configs), 1)
-        proc = cat.process_configs[0]
-        self.assertEqual(proc.name, 'three')
-        self.assertEqual(proc.command, '/bin/pig')
 
 
-        cat = section.process_group_configs[1]
+        cat = section.process_group_configs[0]
         self.assertEqual(len(cat.process_configs), 1)
         self.assertEqual(len(cat.process_configs), 1)
         proc = cat.process_configs[0]
         proc = cat.process_configs[0]
         self.assertEqual(proc.name, 'one')
         self.assertEqual(proc.name, 'one')
@@ -441,6 +442,12 @@ class ServerOptionsTests(unittest.TestCase):
         self.assertTrue(section.process_group_configs is
         self.assertTrue(section.process_group_configs is
                         instance.process_group_configs)
                         instance.process_group_configs)
 
 
+        cat = section.process_group_configs[1]
+        self.assertEqual(len(cat.process_configs), 1)
+        proc = cat.process_configs[0]
+        self.assertEqual(proc.name, 'three')
+        self.assertEqual(proc.command, '/bin/pig')
+
     def test_readFile_failed(self):
     def test_readFile_failed(self):
         from supervisor.options import readFile
         from supervisor.options import readFile
         try:
         try:
@@ -725,6 +732,12 @@ class ServerOptionsTests(unittest.TestCase):
         process_name = %(program_name)s_%(process_num)s
         process_name = %(program_name)s_%(process_num)s
         command = /bin/cat
         command = /bin/cat
         numprocs = 3
         numprocs = 3
+
+        [eventlistener:biz]
+        events=PROCESS_COMMUNICATION
+        process_name = %(program_name)s_%(process_num)s
+        command = /bin/biz
+        numprocs = 2
         """)
         """)
         from supervisor.options import UnhosedConfigParser
         from supervisor.options import UnhosedConfigParser
         from supervisor.dispatchers import default_handler
         from supervisor.dispatchers import default_handler
@@ -732,15 +745,20 @@ class ServerOptionsTests(unittest.TestCase):
         config.read_string(text)
         config.read_string(text)
         instance = self._makeOne()
         instance = self._makeOne()
         gconfigs = instance.process_groups_from_parser(config)
         gconfigs = instance.process_groups_from_parser(config)
-        self.assertEqual(len(gconfigs), 2)
+        self.assertEqual(len(gconfigs), 3)
 
 
         gconfig1 = gconfigs[0]
         gconfig1 = gconfigs[0]
+        self.assertEqual(gconfig1.name, 'biz')
+        self.assertEqual(gconfig1.result_handler, default_handler)
+        self.assertEqual(len(gconfig1.process_configs), 2)
+
+        gconfig1 = gconfigs[1]
         self.assertEqual(gconfig1.name, 'cat')
         self.assertEqual(gconfig1.name, 'cat')
         self.assertEqual(gconfig1.priority, -1)
         self.assertEqual(gconfig1.priority, -1)
         self.assertEqual(gconfig1.result_handler, default_handler)
         self.assertEqual(gconfig1.result_handler, default_handler)
         self.assertEqual(len(gconfig1.process_configs), 3)
         self.assertEqual(len(gconfig1.process_configs), 3)
 
 
-        gconfig1 = gconfigs[1]
+        gconfig1 = gconfigs[2]
         self.assertEqual(gconfig1.name, 'dog')
         self.assertEqual(gconfig1.name, 'dog')
         self.assertEqual(gconfig1.priority, 1)
         self.assertEqual(gconfig1.priority, 1)
         self.assertEqual(gconfig1.result_handler, default_handler)
         self.assertEqual(gconfig1.result_handler, default_handler)
@@ -840,6 +858,7 @@ class ServerOptionsTests(unittest.TestCase):
             return instance.process_groups_from_parser(config)
             return instance.process_groups_from_parser(config)
 
 
         gconfigs = get_process_groups(instance, config)
         gconfigs = get_process_groups(instance, config)
+        
         exp_owner = (sentinel.uid, sentinel.gid)
         exp_owner = (sentinel.uid, sentinel.gid)
 
 
         self.assertEqual(len(gconfigs), 4)
         self.assertEqual(len(gconfigs), 4)
@@ -856,7 +875,7 @@ class ServerOptionsTests(unittest.TestCase):
         pconfig_foo = gconf_foo.process_configs[0]
         pconfig_foo = gconf_foo.process_configs[0]
         self.assertEqual(pconfig_foo.__class__, FastCGIProcessConfig)
         self.assertEqual(pconfig_foo.__class__, FastCGIProcessConfig)
         
         
-        gconf_bar = gconfigs[2]
+        gconf_bar = gconfigs[1]
         self.assertEqual(gconf_bar.name, 'bar')
         self.assertEqual(gconf_bar.name, 'bar')
         self.assertEqual(gconf_bar.priority, 999)
         self.assertEqual(gconf_bar.priority, 999)
         self.assertEqual(gconf_bar.socket_config.url,
         self.assertEqual(gconf_bar.socket_config.url,
@@ -865,7 +884,13 @@ class ServerOptionsTests(unittest.TestCase):
         self.assertEqual(0700, gconf_bar.socket_config.get_mode())
         self.assertEqual(0700, gconf_bar.socket_config.get_mode())
         self.assertEqual(len(gconf_bar.process_configs), 3)
         self.assertEqual(len(gconf_bar.process_configs), 3)
         
         
-        gconf_flub = gconfigs[1]
+        gconf_cub = gconfigs[2]
+        self.assertEqual(gconf_cub.name, 'cub')
+        self.assertEqual(gconf_cub.socket_config.url,
+                         'tcp://localhost:6000')
+        self.assertEqual(len(gconf_cub.process_configs), 1)
+
+        gconf_flub = gconfigs[3]
         self.assertEqual(gconf_flub.name, 'flub')
         self.assertEqual(gconf_flub.name, 'flub')
         self.assertEqual(gconf_flub.socket_config.url,
         self.assertEqual(gconf_flub.socket_config.url,
                          'unix:///tmp/flub.sock')
                          'unix:///tmp/flub.sock')
@@ -873,11 +898,6 @@ class ServerOptionsTests(unittest.TestCase):
         self.assertEqual(0700, gconf_flub.socket_config.get_mode())
         self.assertEqual(0700, gconf_flub.socket_config.get_mode())
         self.assertEqual(len(gconf_flub.process_configs), 1)
         self.assertEqual(len(gconf_flub.process_configs), 1)
         
         
-        gconf_cub = gconfigs[3]
-        self.assertEqual(gconf_cub.name, 'cub')
-        self.assertEqual(gconf_cub.socket_config.url,
-                         'tcp://localhost:6000')
-        self.assertEqual(len(gconf_cub.process_configs), 1)
         
         
 
 
     def test_fcgi_program_no_socket(self):
     def test_fcgi_program_no_socket(self):

+ 3 - 3
src/supervisor/tests/test_supervisorctl.py

@@ -867,9 +867,9 @@ class TestDefaultControllerPlugin(unittest.TestCase):
         self.assertEqual(len(errors), 1)
         self.assertEqual(len(errors), 1)
         error = errors[0]
         error = errors[0]
         self.assertEqual(plugin.listener.closed,
         self.assertEqual(plugin.listener.closed,
-                         'http://localhost:92491/mainlogtail')
+                         'http://localhost:65532/mainlogtail')
         self.assertEqual(error[0],
         self.assertEqual(error[0],
-                         'http://localhost:92491/mainlogtail')
+                         'http://localhost:65532/mainlogtail')
         for msg in ('Cannot connect', 'socket.error'):
         for msg in ('Cannot connect', 'socket.error'):
             self.assertTrue(msg in error[1])
             self.assertTrue(msg in error[1])
 
 
@@ -951,7 +951,7 @@ class DummyPluginFactory:
 class DummyClientOptions:
 class DummyClientOptions:
     def __init__(self):
     def __init__(self):
         self.prompt = 'supervisor'
         self.prompt = 'supervisor'
-        self.serverurl = 'http://localhost:92491' # should be uncontactable
+        self.serverurl = 'http://localhost:65532'
         self.username = 'chrism'
         self.username = 'chrism'
         self.password = '123'
         self.password = '123'
         self.history_file = None
         self.history_file = None