[Twisted-Python] Re: Where is best place to put my custom code?

JD lists at webcrunchers.com
Fri Jan 2 21:13:39 EST 2004


On Jan 1, 2004, at 6:52 PM, Andrew Bennetts wrote:

> On Thu, Jan 01, 2004 at 11:25:37AM -0800, JD wrote:
>> On Dec 31, 2003, at 7:43 PM, Eric Mangold wrote:
>>>
>>> Since this is IRC you shouldn't go issuing commands until your signed
>>> on, thus the signedOn method of your protocol would be the right 
>>> place.
>>
>> Ok,   good point....   but how do I issue commands?   let's say I have
>> a IRCClient class called "testIRC".
>>
>> do I use:    testIRC.sendLine('signedOn')  to send the command to the
>> IRC server
>> to signon?
>
> No.  IRCClient is a LineReceiver subclass, because that's how the 
> low-level
> protocol works.

I think I understand.   what is needed in the docs,   is a better 
description of just
what the roles of these objects are supposed to do,   and where we,  as 
users
of Twisted,  would have a much better chance to use Twisted in the way 
it is
intended.    So the usage of 'sendLine' in a 'lineReceiver' would be 
inappropriate?
but if that's true,  then how does one send a command to the IRC server?

By the way,   I've chosen this as a simple test application,  not 
unlike what I
want to really do,   so I have chosen a relatively simple application,  
where I
just extract all the users of a specified IRC server,  and return the 
data.

I have not specified the form of the data,  that's not important,  but 
what's
important to me,  is the understand how to use Twisted in the way it was
intended.

I have this:

class IRC_test(irc.IRCClient):

     def whois_on(self):
     	"""
     	This method issues a 'WHO' command to see who is on the server.
     	"""
     	self.sendLine("WHO")

     def signedOn(self):
         """Called after sucessfully signing on to the server.   So we 
immediately
         issue the 'whois_on' message to let the server go and do it's 
stuff.
         """
         self.whois_on(self)

     def whois_result(self, prefix,  info):
		"""
		This is the code the IRC is somehow going to call.   Then,
		after it is called,  it stores the info in the factory
		and returns by calling 'reactor.stop()' to exit the reactor.run()
		"""
		self.result = extract_whois_data(info)
		reactor.stop()
		
     def irc_RPL_WHOISSERVER(self, prefix, params):
     	"""
     	This was not included in the parent class IRCClient,
     	So I add it here.  'lineReceived' gets the
     	command = numeric_to_symbolic[command],  which is now
     	the string 'RPL_WHOISSERVER'.   To get THIS method to call,
     	the 'handleCommand' method first generates a key to the
     	class's 'attribute' dictionary,  by pre-pending 'irc_'
     	in front of it to get this method,  which is then called.
     	'params' would then have the result data.   We need to
     	hack around to see what form it's in.
     	"""
         self.whois_result(params[1])
		
def test_irc(host, port):
	"""
	This function scans a particular IRC server,  and returns a dictionary
	of all the nicks and IP addresses.  Nick is the key,  the IP or reverse
	dns is the value.
	"""
	my_nick = "jd6969"
	data_dict = {}		# place where I want my IRC data
	myTestFactory = IRC_test(data_dict,  my_nick)
	reactor.connectTCP(host, port, myTestFactory)
	reactor.run()		# I just wait for the server to respond
	return data_dict	# when it does, I just want to return with the data

I know this is a pretty stupid and useless application,  but this is 
very close to what I want
it to do,  without revealing anything.    I haven't tested this yet,  
because I just want to be
sure I'm doing it right,  rather then to spend weeks down a particular 
path to nowhere.
	
> However, you're not using the low-level protocol, you're
> using an abstraction on top of it -- IRCClient.  Don't break the 
> abstraction
> (by calling LineReceiver's sendLine method) unless you have a good 
> reason to
> and you know the protocol spec well enough to know what you're doing 
> (which
> would be RFC 2812 in this case, I think).

I believe I do,  because I still have to tell the IRC server what I 
want it to do.
And the only way I can see,  is with the sendLine command.

>
> As far as I'm aware, sending a line saying 'signedOn' to the server 
> doesn't
> make any sense in the IRC protocol, so this definitely wouldn't achieve
> anything useful, except causing an error from the server that the 
> IRCClient
> instance probably won't expect (because you broke the abstraction and 
> snuck
> behind its back to do it).

Ok,   so it is clear I'm doing this wrong.  If that's the case,  then 
what is the
RIGHT way of doing it?

>
>> or do I:   testIRC.register('mynick', 'myhost', 'irc.debian.com')
>
> That looks much saner.  And noticeably more full of information than
> testIRC.sendLine('signedOn')...
>
>> then,   when I want to do the "who" command,  would I do:
>> testIRC.sendLine('who')
>
> Well, I believe the WHO command requires an argument, so it would be 
> more
> like testIRC.sendLine('WHO ' + name)... but see my next answer.

hmmm - in checking the RFC it mentions I can either "WHO <channel>" or
"WHO" without specifying a channel,  in which it gets the names of ALL 
the users
of the IRC server not hidden.

>
>> then,   i would expect to call me back,   so I would override the
>> "action" method,
>> like so:
>>
>> def action(self, user, channel, msg):
>> 	< extract the info I want from msg and store it somewhere,  probably
>> in an instance variable in testIRC object >
>>          reactor.stop()    ---  to exit the client and disconnect,   
>> or
>> would I have to put the reactor.stop() in the
>>          factory's "clientConnectionFailed" method,  which I would
>> override of course.
>
> That's not what the action method is for.  As the docstring for action 
> says:
> "Called when I see a user perform an ACTION on a channel."  An ACTION 
> is a
> specific thing in IRC, not a catch-all for every type of message from
> server to client.  See the RFC if you're not familar with the terms.

Yes - I was mislead...   I interpreted this as an action that might come
from the server,  such as the result of a WHO command (in using my 
example)

I think if you take a look at the shell of my application,   I'm no 
longer using this.

> The right way to do this is implemented in sandbox/exarkun/irc2.py -- 
> see
> the AdvancedClient.who method.  I'd follow JP's earlier suggestion and 
> use
> it.

I was NOT aware that anything in "sandbox" had anything to do with IRC.
Expecially when words like this are chosen.   One can interpret 
'sandbox' in
many zillion ways,  so how would I even know this.

I took a look at this,  and WOW - this might be just what I need.   I'm 
going to
have to spend a lot of time studying this.    I had no clue this even 
existed,
primarily because the name 'sandbox' really had nothing to do with IRC,
and I just couldn't make the connection.

> The answer is of course none of those.  You should've already noticed 
> that
> IRCClient doesn't implement a who() method, so it's hardly surprising 
> it
> hasn't implemented a command to deal with its response.

yes - as it also doesn't implement a LOT of things,  and yet they are
defined as overrideable functions.

>  irc2.py does,
> though (and uses Deferreds for dealing with the response, which is 
> cleaner
> anyway).

Kewl - and now just being aware of that,    I definately want to check 
it out.
And also I could use some connents on my above approach,  as I have
avoided the pitfalls you outlined above.

> I thought you wanted to use the WHO command not WHOIS?  Please try to 
> be
> precise.

I thought they were almost the same....  at least that's what I read in 
the RFC's.
What is the difference?

> Anyway, irc2.py also implements WHOIS.

Kewl

>
>> Could I add this to my IRCClient's subclass?
>>
>> IE:
>>
>>     def irc_RPL_WHOISSERVER(self, prefix, params):
>>         self.whois(params[1])
>
> If you look at irc2.py, you'll see it does override this method.  Note 
> that
> 'whois' is a bad name for the response handler -- the existing 
> convention in
> IRCClient is that 'join', 'nick', etc, are the methods that *issue* the
> command, not that handle the response.

I haven't seen any mention of naming conventions,  but if there is one,
I certainly would want to know where it is.

> I think the biggest critical step you're missing is that you're busily
> trying to reinvent a wheel that's already been made in
> sandbox/exarkun/irc2.py, though.

that is obviously true....  I explained above why I just didn't even 
know of it's
existance until it was pointed out to me.

John





More information about the Twisted-Python mailing list