Repository: tornadoweb/tornado
Pull request #3193 (2022-10-07 22:13:59)

Add support for parsing lists of str in config files if multiple=True
Documentation for config file parsing indicates that an option set as comma-separated strings will be parsed into a list of strings when `multiple=True`. This was failing to parse when I attempted to use it. I added tests to catch this case and implemented a fix that would send all of these cases into the `option.parse()` function.

List of strings used in example:
```ini
# Both lists and comma-separated strings are allowed for
# multiple=True.
memcache_hosts = ['cache1.example.com:11011',
                  'cache2.example.com:11011']
memcache_hosts = 'cache1.example.com:11011,cache2.example.com:11011'
```


Patch:
diff --git a/tornado/options.py b/tornado/options.py
index e62f7efe..6ec58996 100644
--- a/tornado/options.py
+++ b/tornado/options.py
@@ -427,7 +427,9 @@ class OptionParser(object):
                             % (option.name, option.type.__name__)
                         )
 
-                if type(config[name]) == str and option.type != str:
+                if type(config[name]) == str and (
+                    option.type != str or option.multiple
+                ):
                     option.parse(config[name])
                 else:
                     option.set(config[name])


Test patch:
diff --git a/tornado/test/options_test.py b/tornado/test/options_test.py
index 1fe58289..6f4021c6 100644
--- a/tornado/test/options_test.py
+++ b/tornado/test/options_test.py
@@ -204,6 +204,7 @@ class OptionsTest(unittest.TestCase):
         options.define("timedelta", type=datetime.timedelta)
         options.define("email", type=Email)
         options.define("list-of-int", type=int, multiple=True)
+        options.define("list-of-str", type=str, multiple=True)
         return options
 
     def _check_options_values(self, options):
@@ -216,6 +217,7 @@ class OptionsTest(unittest.TestCase):
         self.assertEqual(options.email.value, "tornado@web.com")
         self.assertTrue(isinstance(options.email, Email))
         self.assertEqual(options.list_of_int, [1, 2, 3])
+        self.assertEqual(options.list_of_str, ["a", "b", "c"])
 
     def test_types(self):
         options = self._define_options()
@@ -230,6 +232,7 @@ class OptionsTest(unittest.TestCase):
                 "--timedelta=45s",
                 "--email=tornado@web.com",
                 "--list-of-int=1,2,3",
+                "--list-of-str=a,b,c",
             ]
         )
         self._check_options_values(options)
diff --git a/tornado/test/options_test_types.cfg b/tornado/test/options_test_types.cfg
index e1d53cb9..9dfd9220 100644
--- a/tornado/test/options_test_types.cfg
+++ b/tornado/test/options_test_types.cfg
@@ -9,3 +9,4 @@ datetime = datetime(2013, 4, 28, 5, 16)
 timedelta = timedelta(0, 45)
 email = Email('tornado@web.com')
 list_of_int = [1, 2, 3]
+list_of_str = ["a", "b", "c"]
diff --git a/tornado/test/options_test_types_str.cfg b/tornado/test/options_test_types_str.cfg
index 25dfbc2b..b07d6428 100644
--- a/tornado/test/options_test_types_str.cfg
+++ b/tornado/test/options_test_types_str.cfg
@@ -6,3 +6,4 @@ datetime = '2013-04-28 05:16'
 timedelta = '45s'
 email = 'tornado@web.com'
 list_of_int = '1,2,3'
+list_of_str = 'a,b,c'