We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
User objects aren't updated on NICK events
````
<Reptarsaurus> Okay, I just tested....I am doing users = chans[chan].users.values() after
               a nick change, and 'for user in users: print(user.nick)' is showing the
               nick pre-change.
````

This report from Sopel's IRC channel told me something was off in the code that keeps track of users, and indeed something is. Nowhere in the `coretasks` handler for `NICK` events does Sopel update the user object's `nick` property:

https://github.com/sopel-irc/sopel/blob/d4c4c16f850b63776b156fc8c9d3812287b15882/sopel/coretasks.py#L275-L284

`Channel.rename_user()` doesn't touch it either:

https://github.com/sopel-irc/sopel/blob/d4c4c16f850b63776b156fc8c9d3812287b15882/sopel/tools/target.py#L76-L80

I can't find any place in the code where this property gets updated, ever, and that's a bug. Thanks to Reptarsaurus for stumbling onto this, and it should be fixed in the next Sopel version.

--- 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/tools/target.py`
Content:
```
1 # coding=utf-8
2 from __future__ import unicode_literals, absolute_import, print_function, division
3 
4 import functools
5 from sopel.tools import Identifier
6 
7 
8 @functools.total_ordering
9 class User(object):
10     """A representation of a user Sopel is aware of."""
11     def __init__(self, nick, user, host):
12         assert isinstance(nick, Identifier)
13         self.nick = nick
14         """The user's nickname."""
15         self.user = user
16         """The user's local username."""
17         self.host = host
18         """The user's hostname."""
19         self.channels = {}
20         """The channels the user is in.
21 
22         This maps channel name ``Identifier``\\s to ``Channel`` objects."""
23         self.account = None
24         """The IRC services account of the user.
25 
26         This relies on IRCv3 account tracking being enabled."""
27         self.away = None
28         """Whether the user is marked as away."""
29 
30     hostmask = property(lambda self: '{}!{}@{}'.format(self.nick, self.user,
31                                                        self.host))
32     """The user's full hostmask."""
33 
34     def __eq__(self, other):
35         if not isinstance(other, User):
36             return NotImplemented
37         return self.nick == other.nick
38 
39     def __lt__(self, other):
40         if not isinstance(other, User):
41             return NotImplemented
42         return self.nick < other.nick
43 
44 
45 @functools.total_ordering
46 class Channel(object):
47     """A representation of a channel Sopel is in."""
48     def __init__(self, name):
49         assert isinstance(name, Identifier)
50         self.name = name
51         """The name of the channel."""
52         self.users = {}
53         """The users in the channel.
54 
55         This maps username ``Identifier``\\s to channel objects."""
56         self.privileges = {}
57         """The permissions of the users in the channel.
58 
59         This maps username ``Identifier``s to bitwise integer values. This can
60         be compared to appropriate constants from ``sopel.module``."""
61         self.topic = ''
62         """The topic of the channel."""
63 
64     def clear_user(self, nick):
65         user = self.users.pop(nick, None)
66         self.privileges.pop(nick, None)
67         if user is not None:
68             user.channels.pop(self.name, None)
69 
70     def add_user(self, user):
71         assert isinstance(user, User)
72         self.users[user.nick] = user
73         self.privileges[user.nick] = 0
74         user.channels[self.name] = self
75 
76     def rename_user(self, old, new):
77         if old in self.users:
78             self.users[new] = self.users.pop(old)
79         if old in self.privileges:
80             self.privileges[new] = self.privileges.pop(old)
81 
82     def __eq__(self, other):
83         if not isinstance(other, Channel):
84             return NotImplemented
85         return self.name == other.name
86 
87     def __lt__(self, other):
88         if not isinstance(other, Channel):
89             return NotImplemented
90         return self.name < other.name
91
```
--- 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/tools/target.py b/sopel/tools/target.py
--- a/sopel/tools/target.py
+++ b/sopel/tools/target.py
@@ -76,6 +76,7 @@
     def rename_user(self, old, new):
         if old in self.users:
             self.users[new] = self.users.pop(old)
+            self.users[new].nick = new
         if old in self.privileges:
             self.privileges[new] = self.privileges.pop(old)

{"golden_diff": "diff --git a/sopel/tools/target.py b/sopel/tools/target.py\n--- a/sopel/tools/target.py\n+++ b/sopel/tools/target.py\n@@ -76,6 +76,7 @@\n     def rename_user(self, old, new):\n         if old in self.users:\n             self.users[new] = self.users.pop(old)\n+            self.users[new].nick = new\n         if old in self.privileges:\n             self.privileges[new] = self.privileges.pop(old)\n", "issue": "User objects aren't updated on NICK events\n````\r\n<Reptarsaurus> Okay, I just tested....I am doing users = chans[chan].users.values() after\r\n               a nick change, and 'for user in users: print(user.nick)' is showing the\r\n               nick pre-change.\r\n````\r\n\r\nThis report from Sopel's IRC channel told me something was off in the code that keeps track of users, and indeed something is. Nowhere in the `coretasks` handler for `NICK` events does Sopel update the user object's `nick` property:\r\n\r\nhttps://github.com/sopel-irc/sopel/blob/d4c4c16f850b63776b156fc8c9d3812287b15882/sopel/coretasks.py#L275-L284\r\n\r\n`Channel.rename_user()` doesn't touch it either:\r\n\r\nhttps://github.com/sopel-irc/sopel/blob/d4c4c16f850b63776b156fc8c9d3812287b15882/sopel/tools/target.py#L76-L80\r\n\r\nI can't find any place in the code where this property gets updated, ever, and that's a bug. Thanks to Reptarsaurus for stumbling onto this, and it should be fixed in the next Sopel version.\n", "before_files": [{"content": "# coding=utf-8\nfrom __future__ import unicode_literals, absolute_import, print_function, division\n\nimport functools\nfrom sopel.tools import Identifier\n\n\n@functools.total_ordering\nclass User(object):\n    \"\"\"A representation of a user Sopel is aware of.\"\"\"\n    def __init__(self, nick, user, host):\n        assert isinstance(nick, Identifier)\n        self.nick = nick\n        \"\"\"The user's nickname.\"\"\"\n        self.user = user\n        \"\"\"The user's local username.\"\"\"\n        self.host = host\n        \"\"\"The user's hostname.\"\"\"\n        self.channels = {}\n        \"\"\"The channels the user is in.\n\n        This maps channel name ``Identifier``\\\\s to ``Channel`` objects.\"\"\"\n        self.account = None\n        \"\"\"The IRC services account of the user.\n\n        This relies on IRCv3 account tracking being enabled.\"\"\"\n        self.away = None\n        \"\"\"Whether the user is marked as away.\"\"\"\n\n    hostmask = property(lambda self: '{}!{}@{}'.format(self.nick, self.user,\n                                                       self.host))\n    \"\"\"The user's full hostmask.\"\"\"\n\n    def __eq__(self, other):\n        if not isinstance(other, User):\n            return NotImplemented\n        return self.nick == other.nick\n\n    def __lt__(self, other):\n        if not isinstance(other, User):\n            return NotImplemented\n        return self.nick < other.nick\n\n\n@functools.total_ordering\nclass Channel(object):\n    \"\"\"A representation of a channel Sopel is in.\"\"\"\n    def __init__(self, name):\n        assert isinstance(name, Identifier)\n        self.name = name\n        \"\"\"The name of the channel.\"\"\"\n        self.users = {}\n        \"\"\"The users in the channel.\n\n        This maps username ``Identifier``\\\\s to channel objects.\"\"\"\n        self.privileges = {}\n        \"\"\"The permissions of the users in the channel.\n\n        This maps username ``Identifier``s to bitwise integer values. This can\n        be compared to appropriate constants from ``sopel.module``.\"\"\"\n        self.topic = ''\n        \"\"\"The topic of the channel.\"\"\"\n\n    def clear_user(self, nick):\n        user = self.users.pop(nick, None)\n        self.privileges.pop(nick, None)\n        if user is not None:\n            user.channels.pop(self.name, None)\n\n    def add_user(self, user):\n        assert isinstance(user, User)\n        self.users[user.nick] = user\n        self.privileges[user.nick] = 0\n        user.channels[self.name] = self\n\n    def rename_user(self, old, new):\n        if old in self.users:\n            self.users[new] = self.users.pop(old)\n        if old in self.privileges:\n            self.privileges[new] = self.privileges.pop(old)\n\n    def __eq__(self, other):\n        if not isinstance(other, Channel):\n            return NotImplemented\n        return self.name == other.name\n\n    def __lt__(self, other):\n        if not isinstance(other, Channel):\n            return NotImplemented\n        return self.name < other.name\n", "path": "sopel/tools/target.py"}], "after_files": [{"content": "# coding=utf-8\nfrom __future__ import unicode_literals, absolute_import, print_function, division\n\nimport functools\nfrom sopel.tools import Identifier\n\n\n@functools.total_ordering\nclass User(object):\n    \"\"\"A representation of a user Sopel is aware of.\"\"\"\n    def __init__(self, nick, user, host):\n        assert isinstance(nick, Identifier)\n        self.nick = nick\n        \"\"\"The user's nickname.\"\"\"\n        self.user = user\n        \"\"\"The user's local username.\"\"\"\n        self.host = host\n        \"\"\"The user's hostname.\"\"\"\n        self.channels = {}\n        \"\"\"The channels the user is in.\n\n        This maps channel name ``Identifier``\\\\s to ``Channel`` objects.\"\"\"\n        self.account = None\n        \"\"\"The IRC services account of the user.\n\n        This relies on IRCv3 account tracking being enabled.\"\"\"\n        self.away = None\n        \"\"\"Whether the user is marked as away.\"\"\"\n\n    hostmask = property(lambda self: '{}!{}@{}'.format(self.nick, self.user,\n                                                       self.host))\n    \"\"\"The user's full hostmask.\"\"\"\n\n    def __eq__(self, other):\n        if not isinstance(other, User):\n            return NotImplemented\n        return self.nick == other.nick\n\n    def __lt__(self, other):\n        if not isinstance(other, User):\n            return NotImplemented\n        return self.nick < other.nick\n\n\n@functools.total_ordering\nclass Channel(object):\n    \"\"\"A representation of a channel Sopel is in.\"\"\"\n    def __init__(self, name):\n        assert isinstance(name, Identifier)\n        self.name = name\n        \"\"\"The name of the channel.\"\"\"\n        self.users = {}\n        \"\"\"The users in the channel.\n\n        This maps username ``Identifier``\\\\s to channel objects.\"\"\"\n        self.privileges = {}\n        \"\"\"The permissions of the users in the channel.\n\n        This maps username ``Identifier``s to bitwise integer values. This can\n        be compared to appropriate constants from ``sopel.module``.\"\"\"\n        self.topic = ''\n        \"\"\"The topic of the channel.\"\"\"\n\n    def clear_user(self, nick):\n        user = self.users.pop(nick, None)\n        self.privileges.pop(nick, None)\n        if user is not None:\n            user.channels.pop(self.name, None)\n\n    def add_user(self, user):\n        assert isinstance(user, User)\n        self.users[user.nick] = user\n        self.privileges[user.nick] = 0\n        user.channels[self.name] = self\n\n    def rename_user(self, old, new):\n        if old in self.users:\n            self.users[new] = self.users.pop(old)\n            self.users[new].nick = new\n        if old in self.privileges:\n            self.privileges[new] = self.privileges.pop(old)\n\n    def __eq__(self, other):\n        if not isinstance(other, Channel):\n            return NotImplemented\n        return self.name == other.name\n\n    def __lt__(self, other):\n        if not isinstance(other, Channel):\n            return NotImplemented\n        return self.name < other.name\n", "path": "sopel/tools/target.py"}]}