prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  [Bugzilla] Error calling shutdown method for module bugzilla:None
  Noticed this in my logs. Bugzilla shutdown throwing none. On Sopel 6.5.3, Python 3.5.3.
  
  ```
  Ping timeout reached after 120 seconds, closing connection
  Calling shutdown for 2 modules.
  calling reddit.shutdown
  calling bugzilla.shutdown
  Error calling shutdown method for module bugzilla:None
  Closed!
  Warning: Disconnected. Reconnecting in 20 seconds...
   
  Welcome to Sopel. Loading modules...
  ```
  
  --- 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/modules/bugzilla.py`
  Content:
  ```
  1 # coding=utf-8
  2 """Bugzilla issue reporting module
  3 
  4 Copyright 2013-2015, Embolalia, embolalia.com
  5 Licensed under the Eiffel Forum License 2.
  6 """
  7 from __future__ import unicode_literals, absolute_import, print_function, division
  8 
  9 import re
  10 
  11 import xmltodict
  12 
  13 from sopel import web, tools
  14 from sopel.config.types import StaticSection, ListAttribute
  15 from sopel.logger import get_logger
  16 from sopel.module import rule
  17 
  18 
  19 regex = None
  20 LOGGER = get_logger(__name__)
  21 
  22 
  23 class BugzillaSection(StaticSection):
  24     domains = ListAttribute('domains')
  25     """The domains of the Bugzilla instances from which to get information."""
  26 
  27 
  28 def configure(config):
  29     config.define_section('bugzilla', BugzillaSection)
  30     config.bugzilla.configure_setting(
  31         'domains',
  32         'Enter the domains of the Bugzillas you want extra information '
  33         'from (e.g. bugzilla.gnome.org)'
  34     )
  35 
  36 
  37 def setup(bot):
  38     global regex
  39     bot.config.define_section('bugzilla', BugzillaSection)
  40 
  41     if not bot.config.bugzilla.domains:
  42         return
  43     if not bot.memory.contains('url_callbacks'):
  44         bot.memory['url_callbacks'] = tools.SopelMemory()
  45 
  46     domains = '|'.join(bot.config.bugzilla.domains)
  47     regex = re.compile((r'https?://(%s)'
  48                         '(/show_bug.cgi\?\S*?)'
  49                         '(id=\d+)')
  50                        % domains)
  51     bot.memory['url_callbacks'][regex] = show_bug
  52 
  53 
  54 def shutdown(bot):
  55     del bot.memory['url_callbacks'][regex]
  56 
  57 
  58 @rule(r'.*https?://(\S+?)'
  59       '(/show_bug.cgi\?\S*?)'
  60       '(id=\d+).*')
  61 def show_bug(bot, trigger, match=None):
  62     """Show information about a Bugzilla bug."""
  63     match = match or trigger
  64     domain = match.group(1)
  65     if domain not in bot.config.bugzilla.domains:
  66         return
  67     url = 'https://%s%sctype=xml&%s' % match.groups()
  68     data = web.get(url, dont_decode=True)
  69     bug = xmltodict.parse(data).get('bugzilla').get('bug')
  70     error = bug.get('@error', None)  # error="NotPermitted"
  71 
  72     if error:
  73         LOGGER.warning('Bugzilla error: %s' % error)
  74         bot.say('[BUGZILLA] Unable to get infomation for '
  75                 'linked bug (%s)' % error)
  76         return
  77 
  78     message = ('[BUGZILLA] %s | Product: %s | Component: %s | Version: %s | ' +
  79                'Importance: %s |  Status: %s | Assigned to: %s | ' +
  80                'Reported: %s | Modified: %s')
  81 
  82     resolution = bug.get('resolution')
  83     if resolution is not None:
  84         status = bug.get('bug_status') + ' ' + resolution
  85     else:
  86         status = bug.get('bug_status')
  87 
  88     assigned_to = bug.get('assigned_to')
  89     if isinstance(assigned_to, dict):
  90         assigned_to = assigned_to.get('@name')
  91 
  92     message = message % (
  93         bug.get('short_desc'), bug.get('product'),
  94         bug.get('component'), bug.get('version'),
  95         (bug.get('priority') + ' ' + bug.get('bug_severity')),
  96         status, assigned_to, bug.get('creation_ts'),
  97         bug.get('delta_ts'))
  98     bot.say(message)
  99
  ```
  --- 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/modules/bugzilla.py b/sopel/modules/bugzilla.py
  --- a/sopel/modules/bugzilla.py
  +++ b/sopel/modules/bugzilla.py
  @@ -52,7 +52,12 @@
   
   
   def shutdown(bot):
  -    del bot.memory['url_callbacks'][regex]
  +    try:
  +        del bot.memory['url_callbacks'][regex]
  +    except KeyError:
  +        # bot.config.bugzilla.domains was probably just empty on startup
  +        # everything's daijoubu
  +        pass
   
   
   @rule(r'.*https?://(\S+?)'
  
verification_info: {"golden_diff": "diff --git a/sopel/modules/bugzilla.py b/sopel/modules/bugzilla.py\n--- a/sopel/modules/bugzilla.py\n+++ b/sopel/modules/bugzilla.py\n@@ -52,7 +52,12 @@\n \n \n def shutdown(bot):\n-    del bot.memory['url_callbacks'][regex]\n+    try:\n+        del bot.memory['url_callbacks'][regex]\n+    except KeyError:\n+        # bot.config.bugzilla.domains was probably just empty on startup\n+        # everything's daijoubu\n+        pass\n \n \n @rule(r'.*https?://(\\S+?)'\n", "issue": "[Bugzilla] Error calling shutdown method for module bugzilla:None\nNoticed this in my logs. Bugzilla shutdown throwing none. On Sopel 6.5.3, Python 3.5.3.\r\n\r\n```\r\nPing timeout reached after 120 seconds, closing connection\r\nCalling shutdown for 2 modules.\r\ncalling reddit.shutdown\r\ncalling bugzilla.shutdown\r\nError calling shutdown method for module bugzilla:None\r\nClosed!\r\nWarning: Disconnected. Reconnecting in 20 seconds...\r\n \r\nWelcome to Sopel. Loading modules...\r\n```\n", "before_files": [{"content": "# coding=utf-8\n\"\"\"Bugzilla issue reporting module\n\nCopyright 2013-2015, Embolalia, embolalia.com\nLicensed under the Eiffel Forum License 2.\n\"\"\"\nfrom __future__ import unicode_literals, absolute_import, print_function, division\n\nimport re\n\nimport xmltodict\n\nfrom sopel import web, tools\nfrom sopel.config.types import StaticSection, ListAttribute\nfrom sopel.logger import get_logger\nfrom sopel.module import rule\n\n\nregex = None\nLOGGER = get_logger(__name__)\n\n\nclass BugzillaSection(StaticSection):\n    domains = ListAttribute('domains')\n    \"\"\"The domains of the Bugzilla instances from which to get information.\"\"\"\n\n\ndef configure(config):\n    config.define_section('bugzilla', BugzillaSection)\n    config.bugzilla.configure_setting(\n        'domains',\n        'Enter the domains of the Bugzillas you want extra information '\n        'from (e.g. bugzilla.gnome.org)'\n    )\n\n\ndef setup(bot):\n    global regex\n    bot.config.define_section('bugzilla', BugzillaSection)\n\n    if not bot.config.bugzilla.domains:\n        return\n    if not bot.memory.contains('url_callbacks'):\n        bot.memory['url_callbacks'] = tools.SopelMemory()\n\n    domains = '|'.join(bot.config.bugzilla.domains)\n    regex = re.compile((r'https?://(%s)'\n                        '(/show_bug.cgi\\?\\S*?)'\n                        '(id=\\d+)')\n                       % domains)\n    bot.memory['url_callbacks'][regex] = show_bug\n\n\ndef shutdown(bot):\n    del bot.memory['url_callbacks'][regex]\n\n\n@rule(r'.*https?://(\\S+?)'\n      '(/show_bug.cgi\\?\\S*?)'\n      '(id=\\d+).*')\ndef show_bug(bot, trigger, match=None):\n    \"\"\"Show information about a Bugzilla bug.\"\"\"\n    match = match or trigger\n    domain = match.group(1)\n    if domain not in bot.config.bugzilla.domains:\n        return\n    url = 'https://%s%sctype=xml&%s' % match.groups()\n    data = web.get(url, dont_decode=True)\n    bug = xmltodict.parse(data).get('bugzilla').get('bug')\n    error = bug.get('@error', None)  # error=\"NotPermitted\"\n\n    if error:\n        LOGGER.warning('Bugzilla error: %s' % error)\n        bot.say('[BUGZILLA] Unable to get infomation for '\n                'linked bug (%s)' % error)\n        return\n\n    message = ('[BUGZILLA] %s | Product: %s | Component: %s | Version: %s | ' +\n               'Importance: %s |  Status: %s | Assigned to: %s | ' +\n               'Reported: %s | Modified: %s')\n\n    resolution = bug.get('resolution')\n    if resolution is not None:\n        status = bug.get('bug_status') + ' ' + resolution\n    else:\n        status = bug.get('bug_status')\n\n    assigned_to = bug.get('assigned_to')\n    if isinstance(assigned_to, dict):\n        assigned_to = assigned_to.get('@name')\n\n    message = message % (\n        bug.get('short_desc'), bug.get('product'),\n        bug.get('component'), bug.get('version'),\n        (bug.get('priority') + ' ' + bug.get('bug_severity')),\n        status, assigned_to, bug.get('creation_ts'),\n        bug.get('delta_ts'))\n    bot.say(message)\n", "path": "sopel/modules/bugzilla.py"}], "after_files": [{"content": "# coding=utf-8\n\"\"\"Bugzilla issue reporting module\n\nCopyright 2013-2015, Embolalia, embolalia.com\nLicensed under the Eiffel Forum License 2.\n\"\"\"\nfrom __future__ import unicode_literals, absolute_import, print_function, division\n\nimport re\n\nimport xmltodict\n\nfrom sopel import web, tools\nfrom sopel.config.types import StaticSection, ListAttribute\nfrom sopel.logger import get_logger\nfrom sopel.module import rule\n\n\nregex = None\nLOGGER = get_logger(__name__)\n\n\nclass BugzillaSection(StaticSection):\n    domains = ListAttribute('domains')\n    \"\"\"The domains of the Bugzilla instances from which to get information.\"\"\"\n\n\ndef configure(config):\n    config.define_section('bugzilla', BugzillaSection)\n    config.bugzilla.configure_setting(\n        'domains',\n        'Enter the domains of the Bugzillas you want extra information '\n        'from (e.g. bugzilla.gnome.org)'\n    )\n\n\ndef setup(bot):\n    global regex\n    bot.config.define_section('bugzilla', BugzillaSection)\n\n    if not bot.config.bugzilla.domains:\n        return\n    if not bot.memory.contains('url_callbacks'):\n        bot.memory['url_callbacks'] = tools.SopelMemory()\n\n    domains = '|'.join(bot.config.bugzilla.domains)\n    regex = re.compile((r'https?://(%s)'\n                        '(/show_bug.cgi\\?\\S*?)'\n                        '(id=\\d+)')\n                       % domains)\n    bot.memory['url_callbacks'][regex] = show_bug\n\n\ndef shutdown(bot):\n    try:\n        del bot.memory['url_callbacks'][regex]\n    except KeyError:\n        # bot.config.bugzilla.domains was probably just empty on startup\n        # everything's daijoubu\n        pass\n\n\n@rule(r'.*https?://(\\S+?)'\n      '(/show_bug.cgi\\?\\S*?)'\n      '(id=\\d+).*')\ndef show_bug(bot, trigger, match=None):\n    \"\"\"Show information about a Bugzilla bug.\"\"\"\n    match = match or trigger\n    domain = match.group(1)\n    if domain not in bot.config.bugzilla.domains:\n        return\n    url = 'https://%s%sctype=xml&%s' % match.groups()\n    data = web.get(url, dont_decode=True)\n    bug = xmltodict.parse(data).get('bugzilla').get('bug')\n    error = bug.get('@error', None)  # error=\"NotPermitted\"\n\n    if error:\n        LOGGER.warning('Bugzilla error: %s' % error)\n        bot.say('[BUGZILLA] Unable to get infomation for '\n                'linked bug (%s)' % error)\n        return\n\n    message = ('[BUGZILLA] %s | Product: %s | Component: %s | Version: %s | ' +\n               'Importance: %s |  Status: %s | Assigned to: %s | ' +\n               'Reported: %s | Modified: %s')\n\n    resolution = bug.get('resolution')\n    if resolution is not None:\n        status = bug.get('bug_status') + ' ' + resolution\n    else:\n        status = bug.get('bug_status')\n\n    assigned_to = bug.get('assigned_to')\n    if isinstance(assigned_to, dict):\n        assigned_to = assigned_to.get('@name')\n\n    message = message % (\n        bug.get('short_desc'), bug.get('product'),\n        bug.get('component'), bug.get('version'),\n        (bug.get('priority') + ' ' + bug.get('bug_severity')),\n        status, assigned_to, bug.get('creation_ts'),\n        bug.get('delta_ts'))\n    bot.say(message)\n", "path": "sopel/modules/bugzilla.py"}]}