We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Option to specify alternate config directory on first run
When the bot is first run, it executes ```sopel/run_script.py``` at startup.
Said script is hardcoded to create ```~/.sopel``` if it doesn't already exist.
Should there be an option to allow the user to specify an alternate directory to use?

This was observed on d9cfb41 running ```python sopel.py``` without prior configuration.

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

{"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@@ -214,12 +214,12 @@\n \n \n def _wizard(section, config=None):\n-    dotdir = os.path.expanduser('~/.sopel')\n+    dotdir = os.path.dirname(config) if config is not None else DEFAULT_HOMEDIR\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+        _check_dir(dotdir, False)\n         if not os.path.isfile(configpath):\n             print(\"No config file found.\" +\n                   \" Please make one before configuring these options.\")\n@@ -228,15 +228,14 @@\n         config._modules()\n \n \n-def _check_dir(create=True):\n-    dotdir = os.path.join(os.path.expanduser('~'), '.sopel')\n-    if not os.path.isdir(dotdir):\n+def _check_dir(path=DEFAULT_HOMEDIR, create=True):\n+    if not os.path.isdir(path):\n         if create:\n-            print('Creating a config directory at ~/.sopel...')\n+            print('Creating a config directory at {}...'.format(path))\n             try:\n-                os.makedirs(dotdir)\n+                os.makedirs(path)\n             except Exception as e:\n-                print('There was a problem creating %s:' % dotdir, file=sys.stderr)\n+                print('There was a problem creating %s:' % path, 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@@ -246,7 +245,7 @@\n \n \n def _create_config(configpath):\n-    _check_dir()\n+    _check_dir(os.path.dirname(configpath))\n     print(\"Please answer the following questions\" +\n           \" to create your configuration file:\\n\")\n     try:\n", "issue": "Option to specify alternate config directory on first run\nWhen the bot is first run, it executes ```sopel/run_script.py``` at startup.\r\nSaid script is hardcoded to create ```~/.sopel``` if it doesn't already exist.\r\nShould there be an option to allow the user to specify an alternate directory to use?\r\n\r\nThis was observed on d9cfb41 running ```python sopel.py``` without prior configuration.\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\nDEFAULT_HOMEDIR = os.path.join(os.path.expanduser('~'), '.sopel')\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\n    print(\"Config file written successfully!\")\n    return config.filename\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\nDEFAULT_HOMEDIR = os.path.join(os.path.expanduser('~'), '.sopel')\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.dirname(config) if config is not None else DEFAULT_HOMEDIR\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(dotdir, 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(path=DEFAULT_HOMEDIR, create=True):\n    if not os.path.isdir(path):\n        if create:\n            print('Creating a config directory at {}...'.format(path))\n            try:\n                os.makedirs(path)\n            except Exception as e:\n                print('There was a problem creating %s:' % path, 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(os.path.dirname(configpath))\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\n    print(\"Config file written successfully!\")\n    return config.filename\n", "path": "sopel/config/__init__.py"}]}