Merge lp:~isoschiz/endroid/joincleanup into lp:endroid

Proposed by Martin Morrison
Status: Merged
Approved by: ChrisD
Approved revision: 73
Merged at revision: 74
Proposed branch: lp:~isoschiz/endroid/joincleanup
Merge into: lp:endroid
Diff against target: 111 lines (+45/-25)
1 file modified
src/endroid/usermanagement.py (+45/-25)
To merge this branch: bzr merge lp:~isoschiz/endroid/joincleanup
Reviewer Review Type Date Requested Status
ChrisD Approve
Phil Connell Needs Fixing
Review via email: mp+218819@code.launchpad.net

Commit message

Clean up the room join handling to not leak errbacks off the end of the deferred chains in the case of failure.

Description of the change

Clean up the room join handling to not leak errbacks off the end of the deferred chains in the case of failure.

To post a comment you must log in.
Revision history for this message
Phil Connell (pconnell) wrote :

At the top of _retry(), attempts -= 1 is surely a UnboundLocalError?

Why is it useful to return the room name?

review: Needs Fixing
Revision history for this message
Martin Morrison (isoschiz) wrote :

On 08/05/14 16:10, Phil Connell wrote:
> Review: Needs Fixing
>
> At the top of _retry(), attempts -= 1 is surely a UnboundLocalError?

It's an argument to the _retry method, so no. Unless I am missing
something? I have tested this too, and it appears to work. :-)

>
> Why is it useful to return the room name?

It probably isn't. Previously it was returning a string that should have
been logged but wasn't. The room name seemed as good a thing as any to
fire the deferred with. Got a better suggestion?

Revision history for this message
ChrisD (gingerchris) wrote :

Leading underscores on closures seems unnecessary, but meh

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/endroid/usermanagement.py'
2--- src/endroid/usermanagement.py 2013-10-01 18:16:05 +0000
3+++ src/endroid/usermanagement.py 2014-05-08 15:18:21 +0000
4@@ -12,6 +12,8 @@
5 from random import choice
6 from collections import namedtuple
7
8+from twisted.internet import defer
9+
10 # we use ADJECTIVES to generate a random new nick if endroid's is taken
11 ADJECTIVES = [
12 "affronted", "annoyed", "antagonized", "bitter", "chafed", "convulsed",
13@@ -491,38 +493,49 @@
14
15 ### Room functions
16
17- # @@@ at the moment this assumes that if we failed to join a room it was because
18- # our nickname was taken - no provision for wrong passwords (should never be an
19- # issue) or other failures
20- def join_room(self, name, attempts=0):
21+ def join_room(self, name, nick=None, max_attempts=0):
22 """
23- Attempt to join the room 'name'. On failure will try again with
24- different nicknames until attempts == join_attempts.
25+ Attempt to join the room 'name'.
26+
27+ Retries the join up to 'max_attempts' times. If 'max_attempts' is 0,
28+ uses the configured value of 'join_attempts'.
29+
30+ Returns a Deferred that fires with the room name on success, or the
31+ latest reason for failure if all attempts are exhausted.
32
33 """
34 if not name in self.room_rosters:
35- return "Failed to join room {}: not allowed".format(name)
36-
37- def retry(failure, room, nick, attempts):
38- if attempts < self.join_attempts:
39- return self.join_room(room, attempts+1)
40- else:
41- return "Failed to join room {} as {}".format(room, nick)
42- def joined(_, room, nick, attempts):
43- if attempts:
44- self.kick(room, nick, "You've taken EnDroid's nick, "
45- "please reconnect with another nick")
46- self.wh.nick(JID(room), nick)
47- return "Joined room {} as {}".format(room, nick)
48-
49+ return defer.fail(ValueError("{} not in configured rooms"
50+ .format(name)))
51+
52+ if max_attempts == 0:
53+ max_attempts = self.join_attempts
54 room = self.room_rosters[name]
55- nick = room.nick
56- if attempts: # someone has stolen our nickname - generate a new one
57- nick += " ({} and {})".format(choice(ADJECTIVES), choice(ADJECTIVES))
58+ if nick is None:
59+ nick = room.nick
60+
61+ def _retry(failure, room, nick, attempts):
62+ attempts -= 1
63+ if attempts > 0:
64+ nick = room.nick + " ({} and {})".format(choice(ADJECTIVES),
65+ choice(ADJECTIVES))
66+ return self.join_room(room.name, nick, max_attempts=attempts)
67+ else:
68+ return failure
69+ @defer.inlineCallbacks
70+ def _joined(_, room, nick, attempts):
71+ if nick != room.nick:
72+ yield self.kick(room.name, room.nick, "You've taken EnDroid's "
73+ "nick, please reconnect with another nick")
74+ self.wh.nick(JID(room.name), room.nick)
75+ defer.returnValue(room.name)
76
77 d = self.wh.join(JID(name), nick, password=room.password)
78- d.addErrback(retry, name, room.nick, attempts)
79- d.addCallback(joined, name, room.nick, attempts)
80+ # Use addCallbacks here so only one _joined is called in the event of
81+ # potentially multiple retries
82+ d.addCallbacks(_joined, _retry,
83+ callbackArgs=(room, nick, max_attempts),
84+ errbackArgs=(room, nick, max_attempts))
85
86 return d
87
88@@ -540,6 +553,9 @@
89 def failure(_):
90 logging.error("Failed to kick {} from {} ({})".format(
91 nick, room, reason))
92+ #@@@ This swallows errors. Seems expected at the moment, but future
93+ # callers may want to see the error. Make sure to update all
94+ # current callers if this is ever changed.
95
96 return self.wh.kick(JID(room), nick, reason).addCallbacks(success, failure)
97
98@@ -567,9 +583,13 @@
99 # Currently called from elsewhere
100
101 def join_all_rooms(self):
102+ def _fail(fail, room):
103+ logging.error("Failed to join room ({}): {}"
104+ .format(room, str(fail.value)))
105 for room in self.get_rooms():
106 d = self.join_room(room)
107 d.addCallback(lambda _, r: self.joined_room(r), room)
108+ d.addErrback(_fail, room)
109
110 # @@@ This function is in the wrong place. This is the room section
111 def join_all_groups(self):

Subscribers

People subscribed via source and target branches