We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Plugin configuration values cannot handle list of values
The plugin component of the configuration file is set up to be parsed as a JSON list. However, a multi-item list causes a parse exception when Everett parses the configuration.

E.g.:

```
[PLUGINS]
modules = [ "tests.test_plugin" ]
```

works, while

```
[PLUGINS]
modules = [ "tests.test_plugin", "tests.test_plugin" ]
```

does not, and errors with:

```
  File "/opt/src/rastervision/registry.py", line 179, in initialize_config
    verbosity=verbosity)
  File "/opt/src/rastervision/rv_config.py", line 146, in __init__
    ConfigIniEnv(config_file_locations),
  File "/usr/local/lib/python3.5/dist-packages/everett/manager.py", line 602, in __init__
    self.cfg.update(ConfigIniEnv.parse_ini_file(path))
  File "/usr/local/lib/python3.5/dist-packages/everett/manager.py", line 606, in parse_ini_file
    cfgobj = ConfigObj(path)
  File "/usr/local/lib/python3.5/dist-packages/configobj.py", line 1229, in __init__
    self._load(infile, configspec)
  File "/usr/local/lib/python3.5/dist-packages/configobj.py", line 1318, in _load
    raise error
  File "<string>", line None
configobj.ParseError: Parse error in value at line 2.
```

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `rastervision/plugin.py`
Content:
```
1 import os
2 import json
3 import importlib
4 
5 from pluginbase import PluginBase
6 
7 import rastervision as rv
8 from rastervision.protos.plugin_pb2 import PluginConfig as PluginConfigMsg
9 from rastervision.utils.files import download_if_needed
10 
11 
12 class PluginError(Exception):
13     pass
14 
15 
16 class PluginRegistry:
17     @staticmethod
18     def get_instance():
19         return rv._registry._get_plugin_registry()
20 
21     def __init__(self, plugin_config, rv_home):
22         """Initializes this plugin registry.
23 
24         A plugin registry is passed to plugins in a call
25         to their "register_plugin" method.
26 
27         Args:
28            plugin_config - the everett ConfigManager for the plugin
29                            section of the application configuration.
30         """
31         self.plugin_root_dir = os.path.join(rv_home, 'plugins')
32         self.config_builders = {}
33         self.default_raster_sources = []
34         self.default_vector_sources = []
35         self.default_label_sources = []
36         self.default_label_stores = []
37         self.default_evaluators = []
38         self.experiment_runners = {}
39         self.filesystems = []
40 
41         plugin_files = json.loads(plugin_config('files', default='[]'))
42         self._load_from_files(plugin_files)
43         self.plugin_files = plugin_files
44 
45         plugin_modules = json.loads(plugin_config('modules', default='[]'))
46         self._load_from_modules(plugin_modules)
47         self.plugin_modules = plugin_modules
48 
49     def _load_plugin(self, plugin, identifier):
50         # Check the plugin is valid
51         if not hasattr(plugin, 'register_plugin'):
52             raise PluginError('Plugin at {} does not have '
53                               '"register_plugin" method.'.format(identifier))
54 
55         register_method = getattr(plugin, 'register_plugin')
56         if not callable(register_method):
57             raise PluginError('Plugin at {} has a '
58                               '"register_plugin" attribute, '
59                               'but it is not callable'.format(identifier))
60 
61         # TODO: Log loading plugin.
62         register_method(self)
63 
64     def _load_from_files(self, plugin_paths):
65         if not plugin_paths:
66             return
67 
68         self.plugin_sources = []
69 
70         plugin_base = PluginBase(package='rastervision.plugins')
71         for uri in plugin_paths:
72             plugin_name = os.path.splitext(os.path.basename(uri))[0]
73             plugin_path = os.path.join(self.plugin_root_dir, plugin_name)
74             fs = rv._registry.get_file_system(uri, search_plugins=False)
75             local_path = download_if_needed(uri, plugin_path, fs=fs)
76             local_dir = os.path.dirname(local_path)
77 
78             plugin_source = plugin_base.make_plugin_source(
79                 searchpath=[local_dir])
80 
81             # We're required to hang onto the source
82             # to keep it from getting GC'd.
83             self.plugin_sources.append(plugin_source)
84 
85             self._load_plugin(plugin_source.load_plugin(plugin_name), uri)
86 
87     def _load_from_modules(self, plugin_modules):
88         if not plugin_modules:
89             return
90 
91         for module in plugin_modules:
92             plugin = importlib.import_module(module)
93             self._load_plugin(plugin, module)
94 
95     def add_plugins_from_proto(self, plugin_msg):
96         new_plugin_files = list(
97             set(plugin_msg.plugin_uris) - set(self.plugin_files))
98         self._load_from_files(new_plugin_files)
99         self.plugin_files.extend(new_plugin_files)
100 
101         new_plugin_modules = list(
102             set(plugin_msg.plugin_modules) - set(self.plugin_modules))
103         self._load_from_modules(new_plugin_modules)
104         self.plugin_modules.extend(new_plugin_modules)
105 
106     def to_proto(self):
107         """Returns a protobuf message that records the
108         plugin sources for plugins that are currently loaded
109         in the registry.
110         """
111         return PluginConfigMsg(
112             plugin_uris=self.plugin_files, plugin_modules=self.plugin_modules)
113 
114     def register_config_builder(self, group, key, builder_class):
115         """Registers a ConfigBuilder as a plugin.
116 
117         Args:
118            group - The Config group, e.g. rv.BACKEND, rv.TASK.
119            key - The key used for this plugin. This will be used to
120                  construct the builder in a ".builder(key)" call.
121            builder_class - The subclass of ConfigBuilder that builds
122                            the Config for this plugin.
123         """
124         if (group, key) in self.config_builders:
125             raise PluginError('ConfigBuilder already registered for group '
126                               '{} and key {}'.format(group, key))
127         self.config_builders[(group, key)] = builder_class
128 
129     def register_default_raster_source(self, provider_class):
130         """Registers a RasterSourceDefaultProvider for use as a plugin."""
131 
132         self.default_raster_sources.append(provider_class)
133 
134     def register_default_vector_source(self, provider_class):
135         """Registers a VectorSourceDefaultProvider for use as a plugin."""
136         self.default_vector_sources.append(provider_class)
137 
138     def register_default_label_source(self, provider_class):
139         """Registers a LabelSourceDefaultProvider for use as a plugin."""
140         self.default_label_sources.append(provider_class)
141 
142     def register_default_label_store(self, provider_class):
143         """Registers a LabelStoreDefaultProvider for use as a plugin."""
144         self.default_label_stores.append(provider_class)
145 
146     def register_default_evaluator(self, provider_class):
147         """Registers an EvaluatorDefaultProvider for use as a plugin."""
148         self.default_evaluators.append(provider_class)
149 
150     def register_experiment_runner(self, runner_key, runner_class):
151         """Registers an ExperimentRunner as a plugin.
152 
153         Args:
154            runner_key - The key used to reference this plugin runner.
155                         This is a string that will match the command line
156                         argument used to reference this runner; e.g. if the
157                         key is "FOO_RUNNER", then users can use the runner
158                         by issuing a "rastervision run  foo_runner ..." command.
159            runner_class - The class of the ExperimentRunner plugin.
160         """
161         if runner_key in self.experiment_runners:
162             raise PluginError('ExperimentRunner already registered for '
163                               'key {}'.format(runner_key))
164         self.experiment_runners[runner_key] = runner_class
165 
166     def register_filesystem(self, filesystem_class):
167         """Registers a FileSystem as a plugin."""
168         self.filesystems.append(filesystem_class)
169
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/rastervision/plugin.py b/rastervision/plugin.py
--- a/rastervision/plugin.py
+++ b/rastervision/plugin.py
@@ -13,6 +13,24 @@
     pass
 
 
+def load_conf_list(s):
+    """Loads a list of items from the config.
+
+    Lists should be comma separated.
+
+    This takes into account that previous versions of Raster Vision
+    allowed for a `[ "module" ]` like syntax, even though that didn't
+    work for multi-value lists.
+    """
+    try:
+        # A comma separated list of values will be transformed to
+        # having a list-like string, with ' instead of ". Replacing
+        # single quotes with double quotes lets us parse it as a JSON list.
+        return json.loads(s.replace("'", '"'))
+    except json.JSONDecodeError:
+        return list(map(lambda x: x.strip(), s.split(',')))
+
+
 class PluginRegistry:
     @staticmethod
     def get_instance():
@@ -38,11 +56,11 @@
         self.experiment_runners = {}
         self.filesystems = []
 
-        plugin_files = json.loads(plugin_config('files', default='[]'))
+        plugin_files = load_conf_list(plugin_config('files', default='[]'))
         self._load_from_files(plugin_files)
         self.plugin_files = plugin_files
 
-        plugin_modules = json.loads(plugin_config('modules', default='[]'))
+        plugin_modules = load_conf_list(plugin_config('modules', default='[]'))
         self._load_from_modules(plugin_modules)
         self.plugin_modules = plugin_modules

{"golden_diff": "diff --git a/rastervision/plugin.py b/rastervision/plugin.py\n--- a/rastervision/plugin.py\n+++ b/rastervision/plugin.py\n@@ -13,6 +13,24 @@\n     pass\n \n \n+def load_conf_list(s):\n+    \"\"\"Loads a list of items from the config.\n+\n+    Lists should be comma separated.\n+\n+    This takes into account that previous versions of Raster Vision\n+    allowed for a `[ \"module\" ]` like syntax, even though that didn't\n+    work for multi-value lists.\n+    \"\"\"\n+    try:\n+        # A comma separated list of values will be transformed to\n+        # having a list-like string, with ' instead of \". Replacing\n+        # single quotes with double quotes lets us parse it as a JSON list.\n+        return json.loads(s.replace(\"'\", '\"'))\n+    except json.JSONDecodeError:\n+        return list(map(lambda x: x.strip(), s.split(',')))\n+\n+\n class PluginRegistry:\n     @staticmethod\n     def get_instance():\n@@ -38,11 +56,11 @@\n         self.experiment_runners = {}\n         self.filesystems = []\n \n-        plugin_files = json.loads(plugin_config('files', default='[]'))\n+        plugin_files = load_conf_list(plugin_config('files', default='[]'))\n         self._load_from_files(plugin_files)\n         self.plugin_files = plugin_files\n \n-        plugin_modules = json.loads(plugin_config('modules', default='[]'))\n+        plugin_modules = load_conf_list(plugin_config('modules', default='[]'))\n         self._load_from_modules(plugin_modules)\n         self.plugin_modules = plugin_modules\n", "issue": "Plugin configuration values cannot handle list of values\nThe plugin component of the configuration file is set up to be parsed as a JSON list. However, a multi-item list causes a parse exception when Everett parses the configuration.\r\n\r\nE.g.:\r\n\r\n```\r\n[PLUGINS]\r\nmodules = [ \"tests.test_plugin\" ]\r\n```\r\n\r\nworks, while\r\n\r\n```\r\n[PLUGINS]\r\nmodules = [ \"tests.test_plugin\", \"tests.test_plugin\" ]\r\n```\r\n\r\ndoes not, and errors with:\r\n\r\n```\r\n  File \"/opt/src/rastervision/registry.py\", line 179, in initialize_config\r\n    verbosity=verbosity)\r\n  File \"/opt/src/rastervision/rv_config.py\", line 146, in __init__\r\n    ConfigIniEnv(config_file_locations),\r\n  File \"/usr/local/lib/python3.5/dist-packages/everett/manager.py\", line 602, in __init__\r\n    self.cfg.update(ConfigIniEnv.parse_ini_file(path))\r\n  File \"/usr/local/lib/python3.5/dist-packages/everett/manager.py\", line 606, in parse_ini_file\r\n    cfgobj = ConfigObj(path)\r\n  File \"/usr/local/lib/python3.5/dist-packages/configobj.py\", line 1229, in __init__\r\n    self._load(infile, configspec)\r\n  File \"/usr/local/lib/python3.5/dist-packages/configobj.py\", line 1318, in _load\r\n    raise error\r\n  File \"<string>\", line None\r\nconfigobj.ParseError: Parse error in value at line 2.\r\n```\n", "before_files": [{"content": "import os\nimport json\nimport importlib\n\nfrom pluginbase import PluginBase\n\nimport rastervision as rv\nfrom rastervision.protos.plugin_pb2 import PluginConfig as PluginConfigMsg\nfrom rastervision.utils.files import download_if_needed\n\n\nclass PluginError(Exception):\n    pass\n\n\nclass PluginRegistry:\n    @staticmethod\n    def get_instance():\n        return rv._registry._get_plugin_registry()\n\n    def __init__(self, plugin_config, rv_home):\n        \"\"\"Initializes this plugin registry.\n\n        A plugin registry is passed to plugins in a call\n        to their \"register_plugin\" method.\n\n        Args:\n           plugin_config - the everett ConfigManager for the plugin\n                           section of the application configuration.\n        \"\"\"\n        self.plugin_root_dir = os.path.join(rv_home, 'plugins')\n        self.config_builders = {}\n        self.default_raster_sources = []\n        self.default_vector_sources = []\n        self.default_label_sources = []\n        self.default_label_stores = []\n        self.default_evaluators = []\n        self.experiment_runners = {}\n        self.filesystems = []\n\n        plugin_files = json.loads(plugin_config('files', default='[]'))\n        self._load_from_files(plugin_files)\n        self.plugin_files = plugin_files\n\n        plugin_modules = json.loads(plugin_config('modules', default='[]'))\n        self._load_from_modules(plugin_modules)\n        self.plugin_modules = plugin_modules\n\n    def _load_plugin(self, plugin, identifier):\n        # Check the plugin is valid\n        if not hasattr(plugin, 'register_plugin'):\n            raise PluginError('Plugin at {} does not have '\n                              '\"register_plugin\" method.'.format(identifier))\n\n        register_method = getattr(plugin, 'register_plugin')\n        if not callable(register_method):\n            raise PluginError('Plugin at {} has a '\n                              '\"register_plugin\" attribute, '\n                              'but it is not callable'.format(identifier))\n\n        # TODO: Log loading plugin.\n        register_method(self)\n\n    def _load_from_files(self, plugin_paths):\n        if not plugin_paths:\n            return\n\n        self.plugin_sources = []\n\n        plugin_base = PluginBase(package='rastervision.plugins')\n        for uri in plugin_paths:\n            plugin_name = os.path.splitext(os.path.basename(uri))[0]\n            plugin_path = os.path.join(self.plugin_root_dir, plugin_name)\n            fs = rv._registry.get_file_system(uri, search_plugins=False)\n            local_path = download_if_needed(uri, plugin_path, fs=fs)\n            local_dir = os.path.dirname(local_path)\n\n            plugin_source = plugin_base.make_plugin_source(\n                searchpath=[local_dir])\n\n            # We're required to hang onto the source\n            # to keep it from getting GC'd.\n            self.plugin_sources.append(plugin_source)\n\n            self._load_plugin(plugin_source.load_plugin(plugin_name), uri)\n\n    def _load_from_modules(self, plugin_modules):\n        if not plugin_modules:\n            return\n\n        for module in plugin_modules:\n            plugin = importlib.import_module(module)\n            self._load_plugin(plugin, module)\n\n    def add_plugins_from_proto(self, plugin_msg):\n        new_plugin_files = list(\n            set(plugin_msg.plugin_uris) - set(self.plugin_files))\n        self._load_from_files(new_plugin_files)\n        self.plugin_files.extend(new_plugin_files)\n\n        new_plugin_modules = list(\n            set(plugin_msg.plugin_modules) - set(self.plugin_modules))\n        self._load_from_modules(new_plugin_modules)\n        self.plugin_modules.extend(new_plugin_modules)\n\n    def to_proto(self):\n        \"\"\"Returns a protobuf message that records the\n        plugin sources for plugins that are currently loaded\n        in the registry.\n        \"\"\"\n        return PluginConfigMsg(\n            plugin_uris=self.plugin_files, plugin_modules=self.plugin_modules)\n\n    def register_config_builder(self, group, key, builder_class):\n        \"\"\"Registers a ConfigBuilder as a plugin.\n\n        Args:\n           group - The Config group, e.g. rv.BACKEND, rv.TASK.\n           key - The key used for this plugin. This will be used to\n                 construct the builder in a \".builder(key)\" call.\n           builder_class - The subclass of ConfigBuilder that builds\n                           the Config for this plugin.\n        \"\"\"\n        if (group, key) in self.config_builders:\n            raise PluginError('ConfigBuilder already registered for group '\n                              '{} and key {}'.format(group, key))\n        self.config_builders[(group, key)] = builder_class\n\n    def register_default_raster_source(self, provider_class):\n        \"\"\"Registers a RasterSourceDefaultProvider for use as a plugin.\"\"\"\n\n        self.default_raster_sources.append(provider_class)\n\n    def register_default_vector_source(self, provider_class):\n        \"\"\"Registers a VectorSourceDefaultProvider for use as a plugin.\"\"\"\n        self.default_vector_sources.append(provider_class)\n\n    def register_default_label_source(self, provider_class):\n        \"\"\"Registers a LabelSourceDefaultProvider for use as a plugin.\"\"\"\n        self.default_label_sources.append(provider_class)\n\n    def register_default_label_store(self, provider_class):\n        \"\"\"Registers a LabelStoreDefaultProvider for use as a plugin.\"\"\"\n        self.default_label_stores.append(provider_class)\n\n    def register_default_evaluator(self, provider_class):\n        \"\"\"Registers an EvaluatorDefaultProvider for use as a plugin.\"\"\"\n        self.default_evaluators.append(provider_class)\n\n    def register_experiment_runner(self, runner_key, runner_class):\n        \"\"\"Registers an ExperimentRunner as a plugin.\n\n        Args:\n           runner_key - The key used to reference this plugin runner.\n                        This is a string that will match the command line\n                        argument used to reference this runner; e.g. if the\n                        key is \"FOO_RUNNER\", then users can use the runner\n                        by issuing a \"rastervision run  foo_runner ...\" command.\n           runner_class - The class of the ExperimentRunner plugin.\n        \"\"\"\n        if runner_key in self.experiment_runners:\n            raise PluginError('ExperimentRunner already registered for '\n                              'key {}'.format(runner_key))\n        self.experiment_runners[runner_key] = runner_class\n\n    def register_filesystem(self, filesystem_class):\n        \"\"\"Registers a FileSystem as a plugin.\"\"\"\n        self.filesystems.append(filesystem_class)\n", "path": "rastervision/plugin.py"}], "after_files": [{"content": "import os\nimport json\nimport importlib\n\nfrom pluginbase import PluginBase\n\nimport rastervision as rv\nfrom rastervision.protos.plugin_pb2 import PluginConfig as PluginConfigMsg\nfrom rastervision.utils.files import download_if_needed\n\n\nclass PluginError(Exception):\n    pass\n\n\ndef load_conf_list(s):\n    \"\"\"Loads a list of items from the config.\n\n    Lists should be comma separated.\n\n    This takes into account that previous versions of Raster Vision\n    allowed for a `[ \"module\" ]` like syntax, even though that didn't\n    work for multi-value lists.\n    \"\"\"\n    try:\n        # A comma separated list of values will be transformed to\n        # having a list-like string, with ' instead of \". Replacing\n        # single quotes with double quotes lets us parse it as a JSON list.\n        return json.loads(s.replace(\"'\", '\"'))\n    except json.JSONDecodeError:\n        return list(map(lambda x: x.strip(), s.split(',')))\n\n\nclass PluginRegistry:\n    @staticmethod\n    def get_instance():\n        return rv._registry._get_plugin_registry()\n\n    def __init__(self, plugin_config, rv_home):\n        \"\"\"Initializes this plugin registry.\n\n        A plugin registry is passed to plugins in a call\n        to their \"register_plugin\" method.\n\n        Args:\n           plugin_config - the everett ConfigManager for the plugin\n                           section of the application configuration.\n        \"\"\"\n        self.plugin_root_dir = os.path.join(rv_home, 'plugins')\n        self.config_builders = {}\n        self.default_raster_sources = []\n        self.default_vector_sources = []\n        self.default_label_sources = []\n        self.default_label_stores = []\n        self.default_evaluators = []\n        self.experiment_runners = {}\n        self.filesystems = []\n\n        plugin_files = load_conf_list(plugin_config('files', default='[]'))\n        self._load_from_files(plugin_files)\n        self.plugin_files = plugin_files\n\n        plugin_modules = load_conf_list(plugin_config('modules', default='[]'))\n        self._load_from_modules(plugin_modules)\n        self.plugin_modules = plugin_modules\n\n    def _load_plugin(self, plugin, identifier):\n        # Check the plugin is valid\n        if not hasattr(plugin, 'register_plugin'):\n            raise PluginError('Plugin at {} does not have '\n                              '\"register_plugin\" method.'.format(identifier))\n\n        register_method = getattr(plugin, 'register_plugin')\n        if not callable(register_method):\n            raise PluginError('Plugin at {} has a '\n                              '\"register_plugin\" attribute, '\n                              'but it is not callable'.format(identifier))\n\n        # TODO: Log loading plugin.\n        register_method(self)\n\n    def _load_from_files(self, plugin_paths):\n        if not plugin_paths:\n            return\n\n        self.plugin_sources = []\n\n        plugin_base = PluginBase(package='rastervision.plugins')\n        for uri in plugin_paths:\n            plugin_name = os.path.splitext(os.path.basename(uri))[0]\n            plugin_path = os.path.join(self.plugin_root_dir, plugin_name)\n            fs = rv._registry.get_file_system(uri, search_plugins=False)\n            local_path = download_if_needed(uri, plugin_path, fs=fs)\n            local_dir = os.path.dirname(local_path)\n\n            plugin_source = plugin_base.make_plugin_source(\n                searchpath=[local_dir])\n\n            # We're required to hang onto the source\n            # to keep it from getting GC'd.\n            self.plugin_sources.append(plugin_source)\n\n            self._load_plugin(plugin_source.load_plugin(plugin_name), uri)\n\n    def _load_from_modules(self, plugin_modules):\n        if not plugin_modules:\n            return\n\n        for module in plugin_modules:\n            plugin = importlib.import_module(module)\n            self._load_plugin(plugin, module)\n\n    def add_plugins_from_proto(self, plugin_msg):\n        new_plugin_files = list(\n            set(plugin_msg.plugin_uris) - set(self.plugin_files))\n        self._load_from_files(new_plugin_files)\n        self.plugin_files.extend(new_plugin_files)\n\n        new_plugin_modules = list(\n            set(plugin_msg.plugin_modules) - set(self.plugin_modules))\n        self._load_from_modules(new_plugin_modules)\n        self.plugin_modules.extend(new_plugin_modules)\n\n    def to_proto(self):\n        \"\"\"Returns a protobuf message that records the\n        plugin sources for plugins that are currently loaded\n        in the registry.\n        \"\"\"\n        return PluginConfigMsg(\n            plugin_uris=self.plugin_files, plugin_modules=self.plugin_modules)\n\n    def register_config_builder(self, group, key, builder_class):\n        \"\"\"Registers a ConfigBuilder as a plugin.\n\n        Args:\n           group - The Config group, e.g. rv.BACKEND, rv.TASK.\n           key - The key used for this plugin. This will be used to\n                 construct the builder in a \".builder(key)\" call.\n           builder_class - The subclass of ConfigBuilder that builds\n                           the Config for this plugin.\n        \"\"\"\n        if (group, key) in self.config_builders:\n            raise PluginError('ConfigBuilder already registered for group '\n                              '{} and key {}'.format(group, key))\n        self.config_builders[(group, key)] = builder_class\n\n    def register_default_raster_source(self, provider_class):\n        \"\"\"Registers a RasterSourceDefaultProvider for use as a plugin.\"\"\"\n\n        self.default_raster_sources.append(provider_class)\n\n    def register_default_vector_source(self, provider_class):\n        \"\"\"Registers a VectorSourceDefaultProvider for use as a plugin.\"\"\"\n        self.default_vector_sources.append(provider_class)\n\n    def register_default_label_source(self, provider_class):\n        \"\"\"Registers a LabelSourceDefaultProvider for use as a plugin.\"\"\"\n        self.default_label_sources.append(provider_class)\n\n    def register_default_label_store(self, provider_class):\n        \"\"\"Registers a LabelStoreDefaultProvider for use as a plugin.\"\"\"\n        self.default_label_stores.append(provider_class)\n\n    def register_default_evaluator(self, provider_class):\n        \"\"\"Registers an EvaluatorDefaultProvider for use as a plugin.\"\"\"\n        self.default_evaluators.append(provider_class)\n\n    def register_experiment_runner(self, runner_key, runner_class):\n        \"\"\"Registers an ExperimentRunner as a plugin.\n\n        Args:\n           runner_key - The key used to reference this plugin runner.\n                        This is a string that will match the command line\n                        argument used to reference this runner; e.g. if the\n                        key is \"FOO_RUNNER\", then users can use the runner\n                        by issuing a \"rastervision run  foo_runner ...\" command.\n           runner_class - The class of the ExperimentRunner plugin.\n        \"\"\"\n        if runner_key in self.experiment_runners:\n            raise PluginError('ExperimentRunner already registered for '\n                              'key {}'.format(runner_key))\n        self.experiment_runners[runner_key] = runner_class\n\n    def register_filesystem(self, filesystem_class):\n        \"\"\"Registers a FileSystem as a plugin.\"\"\"\n        self.filesystems.append(filesystem_class)\n", "path": "rastervision/plugin.py"}]}