prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Ctrl-C doesn't run shutdown routines
  When pressing <kbd>Ctrl</kbd>-<kbd>C</kbd> to interrupt a Sopel instance running in the foreground of an active shell, it simply prints `KeyboardInterrupt` and exits seemingly without calling any of the shutdown routines.
  
  Pressing <kbd>Ctrl</kbd>-<kbd>C</kbd> should behave more or less the same as `sopel --quit`.
  
  Discovered while testing for #1369.
  
  --- 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/__init__.py`
  Content:
  ```
  1 # coding=utf-8
  2 # ASCII ONLY IN THIS FILE THOUGH!!!!!!!
  3 # Python does some stupid bullshit of respecting LC_ALL over the encoding on the
  4 # file, so in order to undo Python's ridiculous fucking idiocy, we have to have
  5 # our own check.
  6 
  7 # Copyright 2008, Sean B. Palmer, inamidst.com
  8 # Copyright 2012, Elsie Powell, http://embolalia.com
  9 # Copyright 2012, Elad Alfassa <elad@fedoraproject.org>
  10 #
  11 # Licensed under the Eiffel Forum License 2.
  12 
  13 from __future__ import unicode_literals, absolute_import, print_function, division
  14 
  15 import locale
  16 import sys
  17 loc = locale.getlocale()
  18 if sys.version_info.major > 2:
  19     if not loc[1] or 'UTF-8' not in loc[1]:
  20         print('WARNING!!! You are running with a non-UTF8 locale environment '
  21               'variables (e.g. LC_ALL is set to "C"), which makes Python 3 do '
  22               'stupid things. If you get strange errors, please set it to '
  23               'something like "en_US.UTF-8".', file=sys.stderr)
  24 
  25 
  26 from collections import namedtuple
  27 import os
  28 import re
  29 import time
  30 import traceback
  31 import signal
  32 
  33 __version__ = '6.5.3'
  34 
  35 
  36 def _version_info(version=__version__):
  37     regex = re.compile(r'(\d+)\.(\d+)\.(\d+)(?:(a|b|rc)(\d+))?.*')
  38     version_groups = regex.match(__version__).groups()
  39     major, minor, micro = (int(piece) for piece in version_groups[0:3])
  40     level = version_groups[3]
  41     serial = int(version_groups[4] or 0)
  42     if level == 'a':
  43         level = 'alpha'
  44     elif level == 'b':
  45         level = 'beta'
  46     elif level == 'rc':
  47         level = 'candidate'
  48     elif not level and version_groups[4] is None:
  49         level = 'final'
  50     else:
  51         level = 'alpha'
  52     version_type = namedtuple('version_info',
  53                               'major, minor, micro, releaselevel, serial')
  54     return version_type(major, minor, micro, level, serial)
  55 
  56 
  57 version_info = _version_info()
  58 
  59 
  60 def run(config, pid_file, daemon=False):
  61     import sopel.bot as bot
  62     import sopel.logger
  63     from sopel.tools import stderr
  64     delay = 20
  65     # Inject ca_certs from config to web for SSL validation of web requests
  66     if not config.core.ca_certs:
  67         stderr('Could not open CA certificates file. SSL will not '
  68                'work properly.')
  69 
  70     def signal_handler(sig, frame):
  71         if sig == signal.SIGUSR1 or sig == signal.SIGTERM:
  72             stderr('Got quit signal, shutting down.')
  73             p.quit('Closing')
  74     while True:
  75         try:
  76             p = bot.Sopel(config, daemon=daemon)
  77             if hasattr(signal, 'SIGUSR1'):
  78                 signal.signal(signal.SIGUSR1, signal_handler)
  79             if hasattr(signal, 'SIGTERM'):
  80                 signal.signal(signal.SIGTERM, signal_handler)
  81             sopel.logger.setup_logging(p)
  82             p.run(config.core.host, int(config.core.port))
  83         except KeyboardInterrupt:
  84             break
  85         except Exception:  # TODO: Be specific
  86             trace = traceback.format_exc()
  87             try:
  88                 stderr(trace)
  89             except Exception:  # TODO: Be specific
  90                 pass
  91             logfile = open(os.path.join(config.core.logdir, 'exceptions.log'), 'a')
  92             logfile.write('Critical exception in core')
  93             logfile.write(trace)
  94             logfile.write('----------------------------------------\n\n')
  95             logfile.close()
  96             os.unlink(pid_file)
  97             os._exit(1)
  98 
  99         if not isinstance(delay, int):
  100             break
  101         if p.hasquit:
  102             break
  103         stderr('Warning: Disconnected. Reconnecting in %s seconds...' % delay)
  104         time.sleep(delay)
  105     os.unlink(pid_file)
  106     os._exit(0)
  107
  ```
  --- 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()
  
  ```
  
golden_diff: diff --git a/sopel/__init__.py b/sopel/__init__.py
  --- a/sopel/__init__.py
  +++ b/sopel/__init__.py
  @@ -68,7 +68,7 @@
                  'work properly.')
   
       def signal_handler(sig, frame):
  -        if sig == signal.SIGUSR1 or sig == signal.SIGTERM:
  +        if sig == signal.SIGUSR1 or sig == signal.SIGTERM or sig == signal.SIGINT:
               stderr('Got quit signal, shutting down.')
               p.quit('Closing')
       while True:
  @@ -78,6 +78,8 @@
                   signal.signal(signal.SIGUSR1, signal_handler)
               if hasattr(signal, 'SIGTERM'):
                   signal.signal(signal.SIGTERM, signal_handler)
  +            if hasattr(signal, 'SIGINT'):
  +                signal.signal(signal.SIGINT, signal_handler)
               sopel.logger.setup_logging(p)
               p.run(config.core.host, int(config.core.port))
           except KeyboardInterrupt:
  
verification_info: {"golden_diff": "diff --git a/sopel/__init__.py b/sopel/__init__.py\n--- a/sopel/__init__.py\n+++ b/sopel/__init__.py\n@@ -68,7 +68,7 @@\n                'work properly.')\n \n     def signal_handler(sig, frame):\n-        if sig == signal.SIGUSR1 or sig == signal.SIGTERM:\n+        if sig == signal.SIGUSR1 or sig == signal.SIGTERM or sig == signal.SIGINT:\n             stderr('Got quit signal, shutting down.')\n             p.quit('Closing')\n     while True:\n@@ -78,6 +78,8 @@\n                 signal.signal(signal.SIGUSR1, signal_handler)\n             if hasattr(signal, 'SIGTERM'):\n                 signal.signal(signal.SIGTERM, signal_handler)\n+            if hasattr(signal, 'SIGINT'):\n+                signal.signal(signal.SIGINT, signal_handler)\n             sopel.logger.setup_logging(p)\n             p.run(config.core.host, int(config.core.port))\n         except KeyboardInterrupt:\n", "issue": "Ctrl-C doesn't run shutdown routines\nWhen pressing <kbd>Ctrl</kbd>-<kbd>C</kbd> to interrupt a Sopel instance running in the foreground of an active shell, it simply prints `KeyboardInterrupt` and exits seemingly without calling any of the shutdown routines.\r\n\r\nPressing <kbd>Ctrl</kbd>-<kbd>C</kbd> should behave more or less the same as `sopel --quit`.\r\n\r\nDiscovered while testing for #1369.\n", "before_files": [{"content": "# coding=utf-8\n# ASCII ONLY IN THIS FILE THOUGH!!!!!!!\n# Python does some stupid bullshit of respecting LC_ALL over the encoding on the\n# file, so in order to undo Python's ridiculous fucking idiocy, we have to have\n# our own check.\n\n# Copyright 2008, Sean B. Palmer, inamidst.com\n# Copyright 2012, Elsie Powell, http://embolalia.com\n# Copyright 2012, Elad Alfassa <elad@fedoraproject.org>\n#\n# Licensed under the Eiffel Forum License 2.\n\nfrom __future__ import unicode_literals, absolute_import, print_function, division\n\nimport locale\nimport sys\nloc = locale.getlocale()\nif sys.version_info.major > 2:\n    if not loc[1] or 'UTF-8' not in loc[1]:\n        print('WARNING!!! You are running with a non-UTF8 locale environment '\n              'variables (e.g. LC_ALL is set to \"C\"), which makes Python 3 do '\n              'stupid things. If you get strange errors, please set it to '\n              'something like \"en_US.UTF-8\".', file=sys.stderr)\n\n\nfrom collections import namedtuple\nimport os\nimport re\nimport time\nimport traceback\nimport signal\n\n__version__ = '6.5.3'\n\n\ndef _version_info(version=__version__):\n    regex = re.compile(r'(\\d+)\\.(\\d+)\\.(\\d+)(?:(a|b|rc)(\\d+))?.*')\n    version_groups = regex.match(__version__).groups()\n    major, minor, micro = (int(piece) for piece in version_groups[0:3])\n    level = version_groups[3]\n    serial = int(version_groups[4] or 0)\n    if level == 'a':\n        level = 'alpha'\n    elif level == 'b':\n        level = 'beta'\n    elif level == 'rc':\n        level = 'candidate'\n    elif not level and version_groups[4] is None:\n        level = 'final'\n    else:\n        level = 'alpha'\n    version_type = namedtuple('version_info',\n                              'major, minor, micro, releaselevel, serial')\n    return version_type(major, minor, micro, level, serial)\n\n\nversion_info = _version_info()\n\n\ndef run(config, pid_file, daemon=False):\n    import sopel.bot as bot\n    import sopel.logger\n    from sopel.tools import stderr\n    delay = 20\n    # Inject ca_certs from config to web for SSL validation of web requests\n    if not config.core.ca_certs:\n        stderr('Could not open CA certificates file. SSL will not '\n               'work properly.')\n\n    def signal_handler(sig, frame):\n        if sig == signal.SIGUSR1 or sig == signal.SIGTERM:\n            stderr('Got quit signal, shutting down.')\n            p.quit('Closing')\n    while True:\n        try:\n            p = bot.Sopel(config, daemon=daemon)\n            if hasattr(signal, 'SIGUSR1'):\n                signal.signal(signal.SIGUSR1, signal_handler)\n            if hasattr(signal, 'SIGTERM'):\n                signal.signal(signal.SIGTERM, signal_handler)\n            sopel.logger.setup_logging(p)\n            p.run(config.core.host, int(config.core.port))\n        except KeyboardInterrupt:\n            break\n        except Exception:  # TODO: Be specific\n            trace = traceback.format_exc()\n            try:\n                stderr(trace)\n            except Exception:  # TODO: Be specific\n                pass\n            logfile = open(os.path.join(config.core.logdir, 'exceptions.log'), 'a')\n            logfile.write('Critical exception in core')\n            logfile.write(trace)\n            logfile.write('----------------------------------------\\n\\n')\n            logfile.close()\n            os.unlink(pid_file)\n            os._exit(1)\n\n        if not isinstance(delay, int):\n            break\n        if p.hasquit:\n            break\n        stderr('Warning: Disconnected. Reconnecting in %s seconds...' % delay)\n        time.sleep(delay)\n    os.unlink(pid_file)\n    os._exit(0)\n", "path": "sopel/__init__.py"}], "after_files": [{"content": "# coding=utf-8\n# ASCII ONLY IN THIS FILE THOUGH!!!!!!!\n# Python does some stupid bullshit of respecting LC_ALL over the encoding on the\n# file, so in order to undo Python's ridiculous fucking idiocy, we have to have\n# our own check.\n\n# Copyright 2008, Sean B. Palmer, inamidst.com\n# Copyright 2012, Elsie Powell, http://embolalia.com\n# Copyright 2012, Elad Alfassa <elad@fedoraproject.org>\n#\n# Licensed under the Eiffel Forum License 2.\n\nfrom __future__ import unicode_literals, absolute_import, print_function, division\n\nimport locale\nimport sys\nloc = locale.getlocale()\nif sys.version_info.major > 2:\n    if not loc[1] or 'UTF-8' not in loc[1]:\n        print('WARNING!!! You are running with a non-UTF8 locale environment '\n              'variables (e.g. LC_ALL is set to \"C\"), which makes Python 3 do '\n              'stupid things. If you get strange errors, please set it to '\n              'something like \"en_US.UTF-8\".', file=sys.stderr)\n\n\nfrom collections import namedtuple\nimport os\nimport re\nimport time\nimport traceback\nimport signal\n\n__version__ = '6.5.3'\n\n\ndef _version_info(version=__version__):\n    regex = re.compile(r'(\\d+)\\.(\\d+)\\.(\\d+)(?:(a|b|rc)(\\d+))?.*')\n    version_groups = regex.match(__version__).groups()\n    major, minor, micro = (int(piece) for piece in version_groups[0:3])\n    level = version_groups[3]\n    serial = int(version_groups[4] or 0)\n    if level == 'a':\n        level = 'alpha'\n    elif level == 'b':\n        level = 'beta'\n    elif level == 'rc':\n        level = 'candidate'\n    elif not level and version_groups[4] is None:\n        level = 'final'\n    else:\n        level = 'alpha'\n    version_type = namedtuple('version_info',\n                              'major, minor, micro, releaselevel, serial')\n    return version_type(major, minor, micro, level, serial)\n\n\nversion_info = _version_info()\n\n\ndef run(config, pid_file, daemon=False):\n    import sopel.bot as bot\n    import sopel.logger\n    from sopel.tools import stderr\n    delay = 20\n    # Inject ca_certs from config to web for SSL validation of web requests\n    if not config.core.ca_certs:\n        stderr('Could not open CA certificates file. SSL will not '\n               'work properly.')\n\n    def signal_handler(sig, frame):\n        if sig == signal.SIGUSR1 or sig == signal.SIGTERM or sig == signal.SIGINT:\n            stderr('Got quit signal, shutting down.')\n            p.quit('Closing')\n    while True:\n        try:\n            p = bot.Sopel(config, daemon=daemon)\n            if hasattr(signal, 'SIGUSR1'):\n                signal.signal(signal.SIGUSR1, signal_handler)\n            if hasattr(signal, 'SIGTERM'):\n                signal.signal(signal.SIGTERM, signal_handler)\n            if hasattr(signal, 'SIGINT'):\n                signal.signal(signal.SIGINT, signal_handler)\n            sopel.logger.setup_logging(p)\n            p.run(config.core.host, int(config.core.port))\n        except KeyboardInterrupt:\n            break\n        except Exception:  # TODO: Be specific\n            trace = traceback.format_exc()\n            try:\n                stderr(trace)\n            except Exception:  # TODO: Be specific\n                pass\n            logfile = open(os.path.join(config.core.logdir, 'exceptions.log'), 'a')\n            logfile.write('Critical exception in core')\n            logfile.write(trace)\n            logfile.write('----------------------------------------\\n\\n')\n            logfile.close()\n            os.unlink(pid_file)\n            os._exit(1)\n\n        if not isinstance(delay, int):\n            break\n        if p.hasquit:\n            break\n        stderr('Warning: Disconnected. Reconnecting in %s seconds...' % delay)\n        time.sleep(delay)\n    os.unlink(pid_file)\n    os._exit(0)\n", "path": "sopel/__init__.py"}]}