We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Config filename is mis-parsed when running configuration wizard from command line
~~`-w` / `--configure-all` and~~ `--configure-modules` does not drop the file extension `.cfg` from the specified file when searching for a config file to use (unlike just launching the bot, where as the extension does seem to be dropped, or at least accounted for when finding the config file).

A user stopped into #sopel (2019-01-29, ~14:15 UTC-5) who had a `default.cfg` and `custom_config.cfg`. They primarily used `custom_config.cfg` and when attempting to configure modules for said `custom_config.cfg`, the used the command `sopel -c custom_config.cfg --configure-modules`. Sopel could not find `custom_config.cfg.cfg`, and without error/warning, edited `default.cfg`. Launching the bot with `sopel -c custom_config.cfg` still used the correct file though, so any changes made with the configuration wizard were never propogated to the config file that was actually being loaded.

**EDIT**: the `--configure-all` flag will just make a new config file like `custom_config.cfg.cfg`

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `sopel/config/__init__.py`
Content:
```
1 # coding=utf-8
2 """
3 The config object provides a simplified to access Sopel's configuration file.
4 The sections of the file are attributes of the object, and the keys in the
5 section are attributes of that. So, for example, the ``eggs`` attribute in the
6 ``[spam]`` section can be accessed from ``config.spam.eggs``.
7 
8 Section definitions (see "Section configuration sections" below) can be added
9 to the config object with ``define_section``. When this is done, only the
10 defined keys will be available. A section can not be given more than one
11 definition. The ``[core]`` section is defined with ``CoreSection`` when the
12 object is initialized.
13 
14 .. versionadded:: 6.0.0
15 """
16 # Copyright 2012-2015, Elsie Powell, embolalia.com
17 # Copyright © 2012, Elad Alfassa <elad@fedoraproject.org>
18 # Licensed under the Eiffel Forum License 2.
19 
20 from __future__ import unicode_literals, absolute_import, print_function, division
21 
22 from sopel.tools import iteritems, stderr
23 import sopel.tools
24 from sopel.tools import get_input
25 import sopel.loader
26 import os
27 import sys
28 if sys.version_info.major < 3:
29     import ConfigParser
30 else:
31     basestring = str
32     import configparser as ConfigParser
33 import sopel.config.core_section
34 from sopel.config.types import StaticSection
35 
36 
37 class ConfigurationError(Exception):
38     """ Exception type for configuration errors """
39 
40     def __init__(self, value):
41         self.value = value
42 
43     def __str__(self):
44         return 'ConfigurationError: %s' % self.value
45 
46 
47 class Config(object):
48     def __init__(self, filename, validate=True):
49         """The bot's configuration.
50 
51         The given filename will be associated with the configuration, and is
52         the file which will be written if write() is called. If load is not
53         given or True, the configuration object will load the attributes from
54         the file at filename.
55 
56         A few default values will be set here if they are not defined in the
57         config file, or a config file is not loaded. They are documented below.
58         """
59         self.filename = filename
60         """The config object's associated file, as noted above."""
61         self.parser = ConfigParser.RawConfigParser(allow_no_value=True)
62         self.parser.read(self.filename)
63         self.define_section('core', sopel.config.core_section.CoreSection,
64                             validate=validate)
65         self.get = self.parser.get
66 
67     @property
68     def homedir(self):
69         """An alias to config.core.homedir"""
70         # Technically it's the other way around, so we can bootstrap filename
71         # attributes in the core section, but whatever.
72         configured = None
73         if self.parser.has_option('core', 'homedir'):
74             configured = self.parser.get('core', 'homedir')
75         if configured:
76             return configured
77         else:
78             return os.path.dirname(self.filename)
79 
80     def save(self):
81         """Save all changes to the config file."""
82         cfgfile = open(self.filename, 'w')
83         self.parser.write(cfgfile)
84         cfgfile.flush()
85         cfgfile.close()
86 
87     def add_section(self, name):
88         """Add a section to the config file.
89 
90         Returns ``False`` if already exists.
91         """
92         try:
93             return self.parser.add_section(name)
94         except ConfigParser.DuplicateSectionError:
95             return False
96 
97     def define_section(self, name, cls_, validate=True):
98         """Define the available settings in a section.
99 
100         ``cls_`` must be a subclass of ``StaticSection``. If the section has
101         already been defined with a different class, ValueError is raised.
102 
103         If ``validate`` is True, the section's values will be validated, and an
104         exception raised if they are invalid. This is desirable in a module's
105         setup function, for example, but might not be in the configure function.
106         """
107         if not issubclass(cls_, StaticSection):
108             raise ValueError("Class must be a subclass of StaticSection.")
109         current = getattr(self, name, None)
110         current_name = str(current.__class__)
111         new_name = str(cls_)
112         if (current is not None and not isinstance(current, self.ConfigSection) and
113                 not current_name == new_name):
114             raise ValueError(
115                 "Can not re-define class for section from {} to {}.".format(
116                     current_name, new_name)
117             )
118         setattr(self, name, cls_(self, name, validate=validate))
119 
120     class ConfigSection(object):
121 
122         """Represents a section of the config file.
123 
124         Contains all keys in thesection as attributes.
125 
126         """
127 
128         def __init__(self, name, items, parent):
129             object.__setattr__(self, '_name', name)
130             object.__setattr__(self, '_parent', parent)
131             for item in items:
132                 value = item[1].strip()
133                 if not value.lower() == 'none':
134                     if value.lower() == 'false':
135                         value = False
136                     object.__setattr__(self, item[0], value)
137 
138         def __getattr__(self, name):
139             return None
140 
141         def __setattr__(self, name, value):
142             object.__setattr__(self, name, value)
143             if type(value) is list:
144                 value = ','.join(value)
145             self._parent.parser.set(self._name, name, value)
146 
147         def get_list(self, name):
148             value = getattr(self, name)
149             if not value:
150                 return []
151             if isinstance(value, basestring):
152                 value = value.split(',')
153                 # Keep the split value, so we don't have to keep doing this
154                 setattr(self, name, value)
155             return value
156 
157     def __getattr__(self, name):
158         if name in self.parser.sections():
159             items = self.parser.items(name)
160             section = self.ConfigSection(name, items, self)  # Return a section
161             setattr(self, name, section)
162             return section
163         else:
164             raise AttributeError("%r object has no attribute %r"
165                                  % (type(self).__name__, name))
166 
167     def option(self, question, default=False):
168         """Ask "y/n" and return the corresponding boolean answer.
169 
170         Show user in terminal a "y/n" prompt, and return true or false based on
171         the response. If default is passed as true, the default will be shown
172         as ``[y]``, else it will be ``[n]``. ``question`` should be phrased as
173         a question, but without a question mark at the end.
174 
175         """
176         d = 'n'
177         if default:
178             d = 'y'
179         ans = get_input(question + ' (y/n)? [' + d + '] ')
180         if not ans:
181             ans = d
182         return ans.lower() == 'y'
183 
184     def _modules(self):
185         home = os.getcwd()
186         modules_dir = os.path.join(home, 'modules')
187         filenames = sopel.loader.enumerate_modules(self)
188         os.sys.path.insert(0, modules_dir)
189         for name, mod_spec in iteritems(filenames):
190             path, type_ = mod_spec
191             try:
192                 module, _ = sopel.loader.load_module(name, path, type_)
193             except Exception as e:
194                 filename, lineno = sopel.tools.get_raising_file_and_line()
195                 rel_path = os.path.relpath(filename, os.path.dirname(__file__))
196                 raising_stmt = "%s:%d" % (rel_path, lineno)
197                 stderr("Error loading %s: %s (%s)" % (name, e, raising_stmt))
198             else:
199                 if hasattr(module, 'configure'):
200                     prompt = name + ' module'
201                     if module.__doc__:
202                         doc = module.__doc__.split('\n', 1)[0]
203                         if doc:
204                             prompt = doc
205                     prompt = 'Configure {} (y/n)? [n]'.format(prompt)
206                     do_configure = get_input(prompt)
207                     do_configure = do_configure and do_configure.lower() == 'y'
208                     if do_configure:
209                         module.configure(self)
210         self.save()
211 
212 
213 def _wizard(section, config=None):
214     dotdir = os.path.expanduser('~/.sopel')
215     configpath = os.path.join(dotdir, (config or 'default') + '.cfg')
216     if section == 'all':
217         _create_config(configpath)
218     elif section == 'mod':
219         _check_dir(False)
220         if not os.path.isfile(configpath):
221             print("No config file found." +
222                   " Please make one before configuring these options.")
223             sys.exit(1)
224         config = Config(configpath, validate=False)
225         config._modules()
226 
227 
228 def _check_dir(create=True):
229     dotdir = os.path.join(os.path.expanduser('~'), '.sopel')
230     if not os.path.isdir(dotdir):
231         if create:
232             print('Creating a config directory at ~/.sopel...')
233             try:
234                 os.makedirs(dotdir)
235             except Exception as e:
236                 print('There was a problem creating %s:' % dotdir, file=sys.stderr)
237                 print('%s, %s' % (e.__class__, str(e)), file=sys.stderr)
238                 print('Please fix this and then run Sopel again.', file=sys.stderr)
239                 sys.exit(1)
240         else:
241             print("No config file found. Please make one before configuring these options.")
242             sys.exit(1)
243 
244 
245 def _create_config(configpath):
246     _check_dir()
247     print("Please answer the following questions" +
248           " to create your configuration file:\n")
249     try:
250         config = Config(configpath, validate=False)
251         sopel.config.core_section.configure(config)
252         if config.option(
253             'Would you like to see if there are any modules'
254             ' that need configuring'
255         ):
256             config._modules()
257         config.save()
258     except Exception:  # TODO: Be specific
259         print("Encountered an error while writing the config file." +
260               " This shouldn't happen. Check permissions.")
261         raise
262         sys.exit(1)
263     print("Config file written successfully!")
264
```
--- 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/sopel/config/__init__.py b/sopel/config/__init__.py
--- a/sopel/config/__init__.py
+++ b/sopel/config/__init__.py
@@ -212,7 +212,7 @@
 
 def _wizard(section, config=None):
     dotdir = os.path.expanduser('~/.sopel')
-    configpath = os.path.join(dotdir, (config or 'default') + '.cfg')
+    configpath = os.path.join(dotdir, ((config or 'default.cfg') + ('.cfg' if config and not config.endswith('.cfg') else '')))
     if section == 'all':
         _create_config(configpath)
     elif section == 'mod':

{"golden_diff": "diff --git a/sopel/config/__init__.py b/sopel/config/__init__.py\n--- a/sopel/config/__init__.py\n+++ b/sopel/config/__init__.py\n@@ -212,7 +212,7 @@\n \n def _wizard(section, config=None):\n     dotdir = os.path.expanduser('~/.sopel')\n-    configpath = os.path.join(dotdir, (config or 'default') + '.cfg')\n+    configpath = os.path.join(dotdir, ((config or 'default.cfg') + ('.cfg' if config and not config.endswith('.cfg') else '')))\n     if section == 'all':\n         _create_config(configpath)\n     elif section == 'mod':\n", "issue": "Config filename is mis-parsed when running configuration wizard from command line\n~~`-w` / `--configure-all` and~~ `--configure-modules` does not drop the file extension `.cfg` from the specified file when searching for a config file to use (unlike just launching the bot, where as the extension does seem to be dropped, or at least accounted for when finding the config file).\r\n\r\nA user stopped into #sopel (2019-01-29, ~14:15 UTC-5) who had a `default.cfg` and `custom_config.cfg`. They primarily used `custom_config.cfg` and when attempting to configure modules for said `custom_config.cfg`, the used the command `sopel -c custom_config.cfg --configure-modules`. Sopel could not find `custom_config.cfg.cfg`, and without error/warning, edited `default.cfg`. Launching the bot with `sopel -c custom_config.cfg` still used the correct file though, so any changes made with the configuration wizard were never propogated to the config file that was actually being loaded.\r\n\r\n**EDIT**: the `--configure-all` flag will just make a new config file like `custom_config.cfg.cfg`\n", "before_files": [{"content": "# coding=utf-8\n\"\"\"\nThe config object provides a simplified to access Sopel's configuration file.\nThe sections of the file are attributes of the object, and the keys in the\nsection are attributes of that. So, for example, the ``eggs`` attribute in the\n``[spam]`` section can be accessed from ``config.spam.eggs``.\n\nSection definitions (see \"Section configuration sections\" below) can be added\nto the config object with ``define_section``. When this is done, only the\ndefined keys will be available. A section can not be given more than one\ndefinition. The ``[core]`` section is defined with ``CoreSection`` when the\nobject is initialized.\n\n.. versionadded:: 6.0.0\n\"\"\"\n# Copyright 2012-2015, Elsie Powell, embolalia.com\n# Copyright \u00a9 2012, Elad Alfassa <elad@fedoraproject.org>\n# Licensed under the Eiffel Forum License 2.\n\nfrom __future__ import unicode_literals, absolute_import, print_function, division\n\nfrom sopel.tools import iteritems, stderr\nimport sopel.tools\nfrom sopel.tools import get_input\nimport sopel.loader\nimport os\nimport sys\nif sys.version_info.major < 3:\n    import ConfigParser\nelse:\n    basestring = str\n    import configparser as ConfigParser\nimport sopel.config.core_section\nfrom sopel.config.types import StaticSection\n\n\nclass ConfigurationError(Exception):\n    \"\"\" Exception type for configuration errors \"\"\"\n\n    def __init__(self, value):\n        self.value = value\n\n    def __str__(self):\n        return 'ConfigurationError: %s' % self.value\n\n\nclass Config(object):\n    def __init__(self, filename, validate=True):\n        \"\"\"The bot's configuration.\n\n        The given filename will be associated with the configuration, and is\n        the file which will be written if write() is called. If load is not\n        given or True, the configuration object will load the attributes from\n        the file at filename.\n\n        A few default values will be set here if they are not defined in the\n        config file, or a config file is not loaded. They are documented below.\n        \"\"\"\n        self.filename = filename\n        \"\"\"The config object's associated file, as noted above.\"\"\"\n        self.parser = ConfigParser.RawConfigParser(allow_no_value=True)\n        self.parser.read(self.filename)\n        self.define_section('core', sopel.config.core_section.CoreSection,\n                            validate=validate)\n        self.get = self.parser.get\n\n    @property\n    def homedir(self):\n        \"\"\"An alias to config.core.homedir\"\"\"\n        # Technically it's the other way around, so we can bootstrap filename\n        # attributes in the core section, but whatever.\n        configured = None\n        if self.parser.has_option('core', 'homedir'):\n            configured = self.parser.get('core', 'homedir')\n        if configured:\n            return configured\n        else:\n            return os.path.dirname(self.filename)\n\n    def save(self):\n        \"\"\"Save all changes to the config file.\"\"\"\n        cfgfile = open(self.filename, 'w')\n        self.parser.write(cfgfile)\n        cfgfile.flush()\n        cfgfile.close()\n\n    def add_section(self, name):\n        \"\"\"Add a section to the config file.\n\n        Returns ``False`` if already exists.\n        \"\"\"\n        try:\n            return self.parser.add_section(name)\n        except ConfigParser.DuplicateSectionError:\n            return False\n\n    def define_section(self, name, cls_, validate=True):\n        \"\"\"Define the available settings in a section.\n\n        ``cls_`` must be a subclass of ``StaticSection``. If the section has\n        already been defined with a different class, ValueError is raised.\n\n        If ``validate`` is True, the section's values will be validated, and an\n        exception raised if they are invalid. This is desirable in a module's\n        setup function, for example, but might not be in the configure function.\n        \"\"\"\n        if not issubclass(cls_, StaticSection):\n            raise ValueError(\"Class must be a subclass of StaticSection.\")\n        current = getattr(self, name, None)\n        current_name = str(current.__class__)\n        new_name = str(cls_)\n        if (current is not None and not isinstance(current, self.ConfigSection) and\n                not current_name == new_name):\n            raise ValueError(\n                \"Can not re-define class for section from {} to {}.\".format(\n                    current_name, new_name)\n            )\n        setattr(self, name, cls_(self, name, validate=validate))\n\n    class ConfigSection(object):\n\n        \"\"\"Represents a section of the config file.\n\n        Contains all keys in thesection as attributes.\n\n        \"\"\"\n\n        def __init__(self, name, items, parent):\n            object.__setattr__(self, '_name', name)\n            object.__setattr__(self, '_parent', parent)\n            for item in items:\n                value = item[1].strip()\n                if not value.lower() == 'none':\n                    if value.lower() == 'false':\n                        value = False\n                    object.__setattr__(self, item[0], value)\n\n        def __getattr__(self, name):\n            return None\n\n        def __setattr__(self, name, value):\n            object.__setattr__(self, name, value)\n            if type(value) is list:\n                value = ','.join(value)\n            self._parent.parser.set(self._name, name, value)\n\n        def get_list(self, name):\n            value = getattr(self, name)\n            if not value:\n                return []\n            if isinstance(value, basestring):\n                value = value.split(',')\n                # Keep the split value, so we don't have to keep doing this\n                setattr(self, name, value)\n            return value\n\n    def __getattr__(self, name):\n        if name in self.parser.sections():\n            items = self.parser.items(name)\n            section = self.ConfigSection(name, items, self)  # Return a section\n            setattr(self, name, section)\n            return section\n        else:\n            raise AttributeError(\"%r object has no attribute %r\"\n                                 % (type(self).__name__, name))\n\n    def option(self, question, default=False):\n        \"\"\"Ask \"y/n\" and return the corresponding boolean answer.\n\n        Show user in terminal a \"y/n\" prompt, and return true or false based on\n        the response. If default is passed as true, the default will be shown\n        as ``[y]``, else it will be ``[n]``. ``question`` should be phrased as\n        a question, but without a question mark at the end.\n\n        \"\"\"\n        d = 'n'\n        if default:\n            d = 'y'\n        ans = get_input(question + ' (y/n)? [' + d + '] ')\n        if not ans:\n            ans = d\n        return ans.lower() == 'y'\n\n    def _modules(self):\n        home = os.getcwd()\n        modules_dir = os.path.join(home, 'modules')\n        filenames = sopel.loader.enumerate_modules(self)\n        os.sys.path.insert(0, modules_dir)\n        for name, mod_spec in iteritems(filenames):\n            path, type_ = mod_spec\n            try:\n                module, _ = sopel.loader.load_module(name, path, type_)\n            except Exception as e:\n                filename, lineno = sopel.tools.get_raising_file_and_line()\n                rel_path = os.path.relpath(filename, os.path.dirname(__file__))\n                raising_stmt = \"%s:%d\" % (rel_path, lineno)\n                stderr(\"Error loading %s: %s (%s)\" % (name, e, raising_stmt))\n            else:\n                if hasattr(module, 'configure'):\n                    prompt = name + ' module'\n                    if module.__doc__:\n                        doc = module.__doc__.split('\\n', 1)[0]\n                        if doc:\n                            prompt = doc\n                    prompt = 'Configure {} (y/n)? [n]'.format(prompt)\n                    do_configure = get_input(prompt)\n                    do_configure = do_configure and do_configure.lower() == 'y'\n                    if do_configure:\n                        module.configure(self)\n        self.save()\n\n\ndef _wizard(section, config=None):\n    dotdir = os.path.expanduser('~/.sopel')\n    configpath = os.path.join(dotdir, (config or 'default') + '.cfg')\n    if section == 'all':\n        _create_config(configpath)\n    elif section == 'mod':\n        _check_dir(False)\n        if not os.path.isfile(configpath):\n            print(\"No config file found.\" +\n                  \" Please make one before configuring these options.\")\n            sys.exit(1)\n        config = Config(configpath, validate=False)\n        config._modules()\n\n\ndef _check_dir(create=True):\n    dotdir = os.path.join(os.path.expanduser('~'), '.sopel')\n    if not os.path.isdir(dotdir):\n        if create:\n            print('Creating a config directory at ~/.sopel...')\n            try:\n                os.makedirs(dotdir)\n            except Exception as e:\n                print('There was a problem creating %s:' % dotdir, file=sys.stderr)\n                print('%s, %s' % (e.__class__, str(e)), file=sys.stderr)\n                print('Please fix this and then run Sopel again.', file=sys.stderr)\n                sys.exit(1)\n        else:\n            print(\"No config file found. Please make one before configuring these options.\")\n            sys.exit(1)\n\n\ndef _create_config(configpath):\n    _check_dir()\n    print(\"Please answer the following questions\" +\n          \" to create your configuration file:\\n\")\n    try:\n        config = Config(configpath, validate=False)\n        sopel.config.core_section.configure(config)\n        if config.option(\n            'Would you like to see if there are any modules'\n            ' that need configuring'\n        ):\n            config._modules()\n        config.save()\n    except Exception:  # TODO: Be specific\n        print(\"Encountered an error while writing the config file.\" +\n              \" This shouldn't happen. Check permissions.\")\n        raise\n        sys.exit(1)\n    print(\"Config file written successfully!\")\n", "path": "sopel/config/__init__.py"}], "after_files": [{"content": "# coding=utf-8\n\"\"\"\nThe config object provides a simplified to access Sopel's configuration file.\nThe sections of the file are attributes of the object, and the keys in the\nsection are attributes of that. So, for example, the ``eggs`` attribute in the\n``[spam]`` section can be accessed from ``config.spam.eggs``.\n\nSection definitions (see \"Section configuration sections\" below) can be added\nto the config object with ``define_section``. When this is done, only the\ndefined keys will be available. A section can not be given more than one\ndefinition. The ``[core]`` section is defined with ``CoreSection`` when the\nobject is initialized.\n\n.. versionadded:: 6.0.0\n\"\"\"\n# Copyright 2012-2015, Elsie Powell, embolalia.com\n# Copyright \u00a9 2012, Elad Alfassa <elad@fedoraproject.org>\n# Licensed under the Eiffel Forum License 2.\n\nfrom __future__ import unicode_literals, absolute_import, print_function, division\n\nfrom sopel.tools import iteritems, stderr\nimport sopel.tools\nfrom sopel.tools import get_input\nimport sopel.loader\nimport os\nimport sys\nif sys.version_info.major < 3:\n    import ConfigParser\nelse:\n    basestring = str\n    import configparser as ConfigParser\nimport sopel.config.core_section\nfrom sopel.config.types import StaticSection\n\n\nclass ConfigurationError(Exception):\n    \"\"\" Exception type for configuration errors \"\"\"\n\n    def __init__(self, value):\n        self.value = value\n\n    def __str__(self):\n        return 'ConfigurationError: %s' % self.value\n\n\nclass Config(object):\n    def __init__(self, filename, validate=True):\n        \"\"\"The bot's configuration.\n\n        The given filename will be associated with the configuration, and is\n        the file which will be written if write() is called. If load is not\n        given or True, the configuration object will load the attributes from\n        the file at filename.\n\n        A few default values will be set here if they are not defined in the\n        config file, or a config file is not loaded. They are documented below.\n        \"\"\"\n        self.filename = filename\n        \"\"\"The config object's associated file, as noted above.\"\"\"\n        self.parser = ConfigParser.RawConfigParser(allow_no_value=True)\n        self.parser.read(self.filename)\n        self.define_section('core', sopel.config.core_section.CoreSection,\n                            validate=validate)\n        self.get = self.parser.get\n\n    @property\n    def homedir(self):\n        \"\"\"An alias to config.core.homedir\"\"\"\n        # Technically it's the other way around, so we can bootstrap filename\n        # attributes in the core section, but whatever.\n        configured = None\n        if self.parser.has_option('core', 'homedir'):\n            configured = self.parser.get('core', 'homedir')\n        if configured:\n            return configured\n        else:\n            return os.path.dirname(self.filename)\n\n    def save(self):\n        \"\"\"Save all changes to the config file.\"\"\"\n        cfgfile = open(self.filename, 'w')\n        self.parser.write(cfgfile)\n        cfgfile.flush()\n        cfgfile.close()\n\n    def add_section(self, name):\n        \"\"\"Add a section to the config file.\n\n        Returns ``False`` if already exists.\n        \"\"\"\n        try:\n            return self.parser.add_section(name)\n        except ConfigParser.DuplicateSectionError:\n            return False\n\n    def define_section(self, name, cls_, validate=True):\n        \"\"\"Define the available settings in a section.\n\n        ``cls_`` must be a subclass of ``StaticSection``. If the section has\n        already been defined with a different class, ValueError is raised.\n\n        If ``validate`` is True, the section's values will be validated, and an\n        exception raised if they are invalid. This is desirable in a module's\n        setup function, for example, but might not be in the configure function.\n        \"\"\"\n        if not issubclass(cls_, StaticSection):\n            raise ValueError(\"Class must be a subclass of StaticSection.\")\n        current = getattr(self, name, None)\n        current_name = str(current.__class__)\n        new_name = str(cls_)\n        if (current is not None and not isinstance(current, self.ConfigSection) and\n                not current_name == new_name):\n            raise ValueError(\n                \"Can not re-define class for section from {} to {}.\".format(\n                    current_name, new_name)\n            )\n        setattr(self, name, cls_(self, name, validate=validate))\n\n    class ConfigSection(object):\n\n        \"\"\"Represents a section of the config file.\n\n        Contains all keys in thesection as attributes.\n\n        \"\"\"\n\n        def __init__(self, name, items, parent):\n            object.__setattr__(self, '_name', name)\n            object.__setattr__(self, '_parent', parent)\n            for item in items:\n                value = item[1].strip()\n                if not value.lower() == 'none':\n                    if value.lower() == 'false':\n                        value = False\n                    object.__setattr__(self, item[0], value)\n\n        def __getattr__(self, name):\n            return None\n\n        def __setattr__(self, name, value):\n            object.__setattr__(self, name, value)\n            if type(value) is list:\n                value = ','.join(value)\n            self._parent.parser.set(self._name, name, value)\n\n        def get_list(self, name):\n            value = getattr(self, name)\n            if not value:\n                return []\n            if isinstance(value, basestring):\n                value = value.split(',')\n                # Keep the split value, so we don't have to keep doing this\n                setattr(self, name, value)\n            return value\n\n    def __getattr__(self, name):\n        if name in self.parser.sections():\n            items = self.parser.items(name)\n            section = self.ConfigSection(name, items, self)  # Return a section\n            setattr(self, name, section)\n            return section\n        else:\n            raise AttributeError(\"%r object has no attribute %r\"\n                                 % (type(self).__name__, name))\n\n    def option(self, question, default=False):\n        \"\"\"Ask \"y/n\" and return the corresponding boolean answer.\n\n        Show user in terminal a \"y/n\" prompt, and return true or false based on\n        the response. If default is passed as true, the default will be shown\n        as ``[y]``, else it will be ``[n]``. ``question`` should be phrased as\n        a question, but without a question mark at the end.\n\n        \"\"\"\n        d = 'n'\n        if default:\n            d = 'y'\n        ans = get_input(question + ' (y/n)? [' + d + '] ')\n        if not ans:\n            ans = d\n        return ans.lower() == 'y'\n\n    def _modules(self):\n        home = os.getcwd()\n        modules_dir = os.path.join(home, 'modules')\n        filenames = sopel.loader.enumerate_modules(self)\n        os.sys.path.insert(0, modules_dir)\n        for name, mod_spec in iteritems(filenames):\n            path, type_ = mod_spec\n            try:\n                module, _ = sopel.loader.load_module(name, path, type_)\n            except Exception as e:\n                filename, lineno = sopel.tools.get_raising_file_and_line()\n                rel_path = os.path.relpath(filename, os.path.dirname(__file__))\n                raising_stmt = \"%s:%d\" % (rel_path, lineno)\n                stderr(\"Error loading %s: %s (%s)\" % (name, e, raising_stmt))\n            else:\n                if hasattr(module, 'configure'):\n                    prompt = name + ' module'\n                    if module.__doc__:\n                        doc = module.__doc__.split('\\n', 1)[0]\n                        if doc:\n                            prompt = doc\n                    prompt = 'Configure {} (y/n)? [n]'.format(prompt)\n                    do_configure = get_input(prompt)\n                    do_configure = do_configure and do_configure.lower() == 'y'\n                    if do_configure:\n                        module.configure(self)\n        self.save()\n\n\ndef _wizard(section, config=None):\n    dotdir = os.path.expanduser('~/.sopel')\n    configpath = os.path.join(dotdir, ((config or 'default.cfg') + ('.cfg' if config and not config.endswith('.cfg') else '')))\n    if section == 'all':\n        _create_config(configpath)\n    elif section == 'mod':\n        _check_dir(False)\n        if not os.path.isfile(configpath):\n            print(\"No config file found.\" +\n                  \" Please make one before configuring these options.\")\n            sys.exit(1)\n        config = Config(configpath, validate=False)\n        config._modules()\n\n\ndef _check_dir(create=True):\n    dotdir = os.path.join(os.path.expanduser('~'), '.sopel')\n    if not os.path.isdir(dotdir):\n        if create:\n            print('Creating a config directory at ~/.sopel...')\n            try:\n                os.makedirs(dotdir)\n            except Exception as e:\n                print('There was a problem creating %s:' % dotdir, file=sys.stderr)\n                print('%s, %s' % (e.__class__, str(e)), file=sys.stderr)\n                print('Please fix this and then run Sopel again.', file=sys.stderr)\n                sys.exit(1)\n        else:\n            print(\"No config file found. Please make one before configuring these options.\")\n            sys.exit(1)\n\n\ndef _create_config(configpath):\n    _check_dir()\n    print(\"Please answer the following questions\" +\n          \" to create your configuration file:\\n\")\n    try:\n        config = Config(configpath, validate=False)\n        sopel.config.core_section.configure(config)\n        if config.option(\n            'Would you like to see if there are any modules'\n            ' that need configuring'\n        ):\n            config._modules()\n        config.save()\n    except Exception:  # TODO: Be specific\n        print(\"Encountered an error while writing the config file.\" +\n              \" This shouldn't happen. Check permissions.\")\n        raise\n        sys.exit(1)\n    print(\"Config file written successfully!\")\n", "path": "sopel/config/__init__.py"}]}