We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Problem in (at least) Wikipedia module: possibly Unicode related
Hi,
observe the following use case:
    https://en.wikipedia.org/wiki/Hir%C5%8D_Onoda
    @willie_5.4.1 KeyError: u'extract' (file "/usr/local/lib/python2.7/dist-packages/willie-5.4.1-py2.7.egg/willie/modules/wikipedia.py", line 89, in mw_snippet)


--- 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/wikipedia.py`
Content:
```
1 # coding=utf-8
2 """
3 wikipedia.py - Sopel Wikipedia Module
4 Copyright 2013 Edward Powell - embolalia.net
5 Licensed under the Eiffel Forum License 2.
6 
7 http://sopel.chat
8 """
9 from __future__ import unicode_literals, absolute_import, print_function, division
10 from sopel import web, tools
11 from sopel.config.types import StaticSection, ValidatedAttribute
12 from sopel.module import NOLIMIT, commands, example, rule
13 import json
14 import re
15 
16 import sys
17 if sys.version_info.major < 3:
18     from urlparse import unquote
19 else:
20     from urllib.parse import unquote
21 
22 REDIRECT = re.compile(r'^REDIRECT (.*)')
23 
24 
25 class WikipediaSection(StaticSection):
26     default_lang = ValidatedAttribute('default_lang', default='en')
27     """The default language to find articles from."""
28     lang_per_channel = ValidatedAttribute('lang_per_channel')
29 
30 
31 def setup(bot):
32     bot.config.define_section('wikipedia', WikipediaSection)
33 
34     regex = re.compile('([a-z]+).(wikipedia.org/wiki/)([^ ]+)')
35     if not bot.memory.contains('url_callbacks'):
36         bot.memory['url_callbacks'] = tools.SopelMemory()
37     bot.memory['url_callbacks'][regex] = mw_info
38 
39 
40 def configure(config):
41     config.define_section('wikipedia', WikipediaSection)
42     config.wikipedia.configure_setting(
43         'default_lang',
44         "Enter the default language to find articles from."
45     )
46 
47 
48 def mw_search(server, query, num):
49     """
50     Searches the specified MediaWiki server for the given query, and returns
51     the specified number of results.
52     """
53     search_url = ('http://%s/w/api.php?format=json&action=query'
54                   '&list=search&srlimit=%d&srprop=timestamp&srwhat=text'
55                   '&srsearch=') % (server, num)
56     search_url += query
57     query = json.loads(web.get(search_url))
58     if 'query' in query:
59         query = query['query']['search']
60         return [r['title'] for r in query]
61     else:
62         return None
63 
64 
65 def say_snippet(bot, server, query, show_url=True):
66     page_name = query.replace('_', ' ')
67     query = query.replace(' ', '_')
68     snippet = mw_snippet(server, query)
69     msg = '[WIKIPEDIA] {} | "{}"'.format(page_name, snippet)
70     if show_url:
71         msg = msg + ' | https://{}/wiki/{}'.format(server, query)
72     bot.say(msg)
73 
74 
75 def mw_snippet(server, query):
76     """
77     Retrives a snippet of the specified length from the given page on the given
78     server.
79     """
80     snippet_url = ('https://' + server + '/w/api.php?format=json'
81                    '&action=query&prop=extracts&exintro&explaintext'
82                    '&exchars=300&redirects&titles=')
83     snippet_url += query
84     snippet = json.loads(web.get(snippet_url))
85     snippet = snippet['query']['pages']
86 
87     # For some reason, the API gives the page *number* as the key, so we just
88     # grab the first page number in the results.
89     snippet = snippet[list(snippet.keys())[0]]
90 
91     return snippet['extract']
92 
93 
94 @rule('.*/([a-z]+\.wikipedia.org)/wiki/([^ ]+).*')
95 def mw_info(bot, trigger, found_match=None):
96     """
97     Retrives a snippet of the specified length from the given page on the given
98     server.
99     """
100     match = found_match or trigger
101     say_snippet(bot, match.group(1), unquote(match.group(2)), show_url=False)
102 
103 
104 @commands('w', 'wiki', 'wik')
105 @example('.w San Francisco')
106 def wikipedia(bot, trigger):
107     lang = bot.config.wikipedia.default_lang
108 
109     #change lang if channel has custom language set
110     if (trigger.sender and not trigger.sender.is_nick() and
111             bot.config.wikipedia.lang_per_channel):
112         customlang = re.search('(' + trigger.sender + '):(\w+)',
113                                bot.config.wikipedia.lang_per_channel)
114         if customlang is not None:
115             lang = customlang.group(2)
116 
117     if trigger.group(2) is None:
118         bot.reply("What do you want me to look up?")
119         return NOLIMIT
120 
121     query = trigger.group(2)
122     args = re.search(r'^-([a-z]{2,12})\s(.*)', query)
123     if args is not None:
124         lang = args.group(1)
125         query = args.group(2)
126 
127     if not query:
128         bot.reply('What do you want me to look up?')
129         return NOLIMIT
130     server = lang + '.wikipedia.org'
131     query = mw_search(server, query, 1)
132     if not query:
133         bot.reply("I can't find any results for that.")
134         return NOLIMIT
135     else:
136         query = query[0]
137     say_snippet(bot, server, query)
138
```
--- 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/modules/wikipedia.py b/sopel/modules/wikipedia.py
--- a/sopel/modules/wikipedia.py
+++ b/sopel/modules/wikipedia.py
@@ -15,7 +15,8 @@
 
 import sys
 if sys.version_info.major < 3:
-    from urlparse import unquote
+    from urlparse import unquote as _unquote
+    unquote = lambda s: _unquote(s.encode('utf-8')).decode('utf-8')
 else:
     from urllib.parse import unquote

{"golden_diff": "diff --git a/sopel/modules/wikipedia.py b/sopel/modules/wikipedia.py\n--- a/sopel/modules/wikipedia.py\n+++ b/sopel/modules/wikipedia.py\n@@ -15,7 +15,8 @@\n \n import sys\n if sys.version_info.major < 3:\n-    from urlparse import unquote\n+    from urlparse import unquote as _unquote\n+    unquote = lambda s: _unquote(s.encode('utf-8')).decode('utf-8')\n else:\n     from urllib.parse import unquote\n", "issue": "Problem in (at least) Wikipedia module: possibly Unicode related\nHi,\nobserve the following use case:\n    https://en.wikipedia.org/wiki/Hir%C5%8D_Onoda\n    @willie_5.4.1 KeyError: u'extract' (file \"/usr/local/lib/python2.7/dist-packages/willie-5.4.1-py2.7.egg/willie/modules/wikipedia.py\", line 89, in mw_snippet)\n\n", "before_files": [{"content": "# coding=utf-8\n\"\"\"\nwikipedia.py - Sopel Wikipedia Module\nCopyright 2013 Edward Powell - embolalia.net\nLicensed under the Eiffel Forum License 2.\n\nhttp://sopel.chat\n\"\"\"\nfrom __future__ import unicode_literals, absolute_import, print_function, division\nfrom sopel import web, tools\nfrom sopel.config.types import StaticSection, ValidatedAttribute\nfrom sopel.module import NOLIMIT, commands, example, rule\nimport json\nimport re\n\nimport sys\nif sys.version_info.major < 3:\n    from urlparse import unquote\nelse:\n    from urllib.parse import unquote\n\nREDIRECT = re.compile(r'^REDIRECT (.*)')\n\n\nclass WikipediaSection(StaticSection):\n    default_lang = ValidatedAttribute('default_lang', default='en')\n    \"\"\"The default language to find articles from.\"\"\"\n    lang_per_channel = ValidatedAttribute('lang_per_channel')\n\n\ndef setup(bot):\n    bot.config.define_section('wikipedia', WikipediaSection)\n\n    regex = re.compile('([a-z]+).(wikipedia.org/wiki/)([^ ]+)')\n    if not bot.memory.contains('url_callbacks'):\n        bot.memory['url_callbacks'] = tools.SopelMemory()\n    bot.memory['url_callbacks'][regex] = mw_info\n\n\ndef configure(config):\n    config.define_section('wikipedia', WikipediaSection)\n    config.wikipedia.configure_setting(\n        'default_lang',\n        \"Enter the default language to find articles from.\"\n    )\n\n\ndef mw_search(server, query, num):\n    \"\"\"\n    Searches the specified MediaWiki server for the given query, and returns\n    the specified number of results.\n    \"\"\"\n    search_url = ('http://%s/w/api.php?format=json&action=query'\n                  '&list=search&srlimit=%d&srprop=timestamp&srwhat=text'\n                  '&srsearch=') % (server, num)\n    search_url += query\n    query = json.loads(web.get(search_url))\n    if 'query' in query:\n        query = query['query']['search']\n        return [r['title'] for r in query]\n    else:\n        return None\n\n\ndef say_snippet(bot, server, query, show_url=True):\n    page_name = query.replace('_', ' ')\n    query = query.replace(' ', '_')\n    snippet = mw_snippet(server, query)\n    msg = '[WIKIPEDIA] {} | \"{}\"'.format(page_name, snippet)\n    if show_url:\n        msg = msg + ' | https://{}/wiki/{}'.format(server, query)\n    bot.say(msg)\n\n\ndef mw_snippet(server, query):\n    \"\"\"\n    Retrives a snippet of the specified length from the given page on the given\n    server.\n    \"\"\"\n    snippet_url = ('https://' + server + '/w/api.php?format=json'\n                   '&action=query&prop=extracts&exintro&explaintext'\n                   '&exchars=300&redirects&titles=')\n    snippet_url += query\n    snippet = json.loads(web.get(snippet_url))\n    snippet = snippet['query']['pages']\n\n    # For some reason, the API gives the page *number* as the key, so we just\n    # grab the first page number in the results.\n    snippet = snippet[list(snippet.keys())[0]]\n\n    return snippet['extract']\n\n\n@rule('.*/([a-z]+\\.wikipedia.org)/wiki/([^ ]+).*')\ndef mw_info(bot, trigger, found_match=None):\n    \"\"\"\n    Retrives a snippet of the specified length from the given page on the given\n    server.\n    \"\"\"\n    match = found_match or trigger\n    say_snippet(bot, match.group(1), unquote(match.group(2)), show_url=False)\n\n\n@commands('w', 'wiki', 'wik')\n@example('.w San Francisco')\ndef wikipedia(bot, trigger):\n    lang = bot.config.wikipedia.default_lang\n\n    #change lang if channel has custom language set\n    if (trigger.sender and not trigger.sender.is_nick() and\n            bot.config.wikipedia.lang_per_channel):\n        customlang = re.search('(' + trigger.sender + '):(\\w+)',\n                               bot.config.wikipedia.lang_per_channel)\n        if customlang is not None:\n            lang = customlang.group(2)\n\n    if trigger.group(2) is None:\n        bot.reply(\"What do you want me to look up?\")\n        return NOLIMIT\n\n    query = trigger.group(2)\n    args = re.search(r'^-([a-z]{2,12})\\s(.*)', query)\n    if args is not None:\n        lang = args.group(1)\n        query = args.group(2)\n\n    if not query:\n        bot.reply('What do you want me to look up?')\n        return NOLIMIT\n    server = lang + '.wikipedia.org'\n    query = mw_search(server, query, 1)\n    if not query:\n        bot.reply(\"I can't find any results for that.\")\n        return NOLIMIT\n    else:\n        query = query[0]\n    say_snippet(bot, server, query)\n", "path": "sopel/modules/wikipedia.py"}], "after_files": [{"content": "# coding=utf-8\n\"\"\"\nwikipedia.py - Sopel Wikipedia Module\nCopyright 2013 Edward Powell - embolalia.net\nLicensed under the Eiffel Forum License 2.\n\nhttp://sopel.chat\n\"\"\"\nfrom __future__ import unicode_literals, absolute_import, print_function, division\nfrom sopel import web, tools\nfrom sopel.config.types import StaticSection, ValidatedAttribute\nfrom sopel.module import NOLIMIT, commands, example, rule\nimport json\nimport re\n\nimport sys\nif sys.version_info.major < 3:\n    from urlparse import unquote as _unquote\n    unquote = lambda s: _unquote(s.encode('utf-8')).decode('utf-8')\nelse:\n    from urllib.parse import unquote\n\nREDIRECT = re.compile(r'^REDIRECT (.*)')\n\n\nclass WikipediaSection(StaticSection):\n    default_lang = ValidatedAttribute('default_lang', default='en')\n    \"\"\"The default language to find articles from.\"\"\"\n    lang_per_channel = ValidatedAttribute('lang_per_channel')\n\n\ndef setup(bot):\n    bot.config.define_section('wikipedia', WikipediaSection)\n\n    regex = re.compile('([a-z]+).(wikipedia.org/wiki/)([^ ]+)')\n    if not bot.memory.contains('url_callbacks'):\n        bot.memory['url_callbacks'] = tools.SopelMemory()\n    bot.memory['url_callbacks'][regex] = mw_info\n\n\ndef configure(config):\n    config.define_section('wikipedia', WikipediaSection)\n    config.wikipedia.configure_setting(\n        'default_lang',\n        \"Enter the default language to find articles from.\"\n    )\n\n\ndef mw_search(server, query, num):\n    \"\"\"\n    Searches the specified MediaWiki server for the given query, and returns\n    the specified number of results.\n    \"\"\"\n    search_url = ('http://%s/w/api.php?format=json&action=query'\n                  '&list=search&srlimit=%d&srprop=timestamp&srwhat=text'\n                  '&srsearch=') % (server, num)\n    search_url += query\n    query = json.loads(web.get(search_url))\n    if 'query' in query:\n        query = query['query']['search']\n        return [r['title'] for r in query]\n    else:\n        return None\n\n\ndef say_snippet(bot, server, query, show_url=True):\n    page_name = query.replace('_', ' ')\n    query = query.replace(' ', '_')\n    snippet = mw_snippet(server, query)\n    msg = '[WIKIPEDIA] {} | \"{}\"'.format(page_name, snippet)\n    if show_url:\n        msg = msg + ' | https://{}/wiki/{}'.format(server, query)\n    bot.say(msg)\n\n\ndef mw_snippet(server, query):\n    \"\"\"\n    Retrives a snippet of the specified length from the given page on the given\n    server.\n    \"\"\"\n    snippet_url = ('https://' + server + '/w/api.php?format=json'\n                   '&action=query&prop=extracts&exintro&explaintext'\n                   '&exchars=300&redirects&titles=')\n    snippet_url += query\n    snippet = json.loads(web.get(snippet_url))\n    snippet = snippet['query']['pages']\n\n    # For some reason, the API gives the page *number* as the key, so we just\n    # grab the first page number in the results.\n    snippet = snippet[list(snippet.keys())[0]]\n\n    return snippet['extract']\n\n\n@rule('.*/([a-z]+\\.wikipedia.org)/wiki/([^ ]+).*')\ndef mw_info(bot, trigger, found_match=None):\n    \"\"\"\n    Retrives a snippet of the specified length from the given page on the given\n    server.\n    \"\"\"\n    match = found_match or trigger\n    say_snippet(bot, match.group(1), unquote(match.group(2)), show_url=False)\n\n\n@commands('w', 'wiki', 'wik')\n@example('.w San Francisco')\ndef wikipedia(bot, trigger):\n    lang = bot.config.wikipedia.default_lang\n\n    #change lang if channel has custom language set\n    if (trigger.sender and not trigger.sender.is_nick() and\n            bot.config.wikipedia.lang_per_channel):\n        customlang = re.search('(' + trigger.sender + '):(\\w+)',\n                               bot.config.wikipedia.lang_per_channel)\n        if customlang is not None:\n            lang = customlang.group(2)\n\n    if trigger.group(2) is None:\n        bot.reply(\"What do you want me to look up?\")\n        return NOLIMIT\n\n    query = trigger.group(2)\n    args = re.search(r'^-([a-z]{2,12})\\s(.*)', query)\n    if args is not None:\n        lang = args.group(1)\n        query = args.group(2)\n\n    if not query:\n        bot.reply('What do you want me to look up?')\n        return NOLIMIT\n    server = lang + '.wikipedia.org'\n    query = mw_search(server, query, 1)\n    if not query:\n        bot.reply(\"I can't find any results for that.\")\n        return NOLIMIT\n    else:\n        query = query[0]\n    say_snippet(bot, server, query)\n", "path": "sopel/modules/wikipedia.py"}]}