prompt: You will be provided with a partial code base and an issue statement explaining a problem to resolve.
  <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.
  
  </issue>
  <code>
  [start of sopel/tools/target.py]
  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 of sopel/tools/target.py]
  </code>
  
  I need you to solve this issue by generating a single patch file that I can apply directly to this repository using git apply. Please respond with a single patch file in the following format.
  <patch>
  diff --git a/file.py b/file.py
  --- a/file.py
  +++ b/file.py
  @@ -1,27 +1,35 @@
   def euclidean(a, b):
  -    while b:
  -        a, b = b, a % b
  -    return a
  +    if b == 0:
  +        return a
  +    return euclidean(b, a % b)
   
   
   def bresenham(x0, y0, x1, y1):
       points = []
       dx = abs(x1 - x0)
       dy = abs(y1 - y0)
  -    sx = 1 if x0 < x1 else -1
  -    sy = 1 if y0 < y1 else -1
  -    err = dx - dy
  +    x, y = x0, y0
  +    sx = -1 if x0 > x1 else 1
  +    sy = -1 if y0 > y1 else 1
   
  -    while True:
  -        points.append((x0, y0))
  -        if x0 == x1 and y0 == y1:
  -            break
  -        e2 = 2 * err
  -        if e2 > -dy:
  -            err -= dy
  -            x0 += sx
  -        if e2 < dx:
  -            err += dx
  -            y0 += sy
  +    if dx > dy:
  +        err = dx / 2.0
  +        while x != x1:
  +            points.append((x, y))
  +            err -= dy
  +            if err < 0:
  +                y += sy
  +                err += dx
  +            x += sx
  +    else:
  +        err = dy / 2.0
  +        while y != y1:
  +            points.append((x, y))
  +            err -= dx
  +            if err < 0:
  +                x += sx
  +                err += dy
  +            y += sy
  +
  +    points.append((x, y))
       return points
  
  </patch>
golden_diff: 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)
  
verification_info: {"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"}]}