<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML><HEAD><TITLE>Learning about IPushProducer</TITLE>
<META http-equiv=Content-Type content="text/html; charset=us-ascii">
<META content="MSHTML 6.00.2900.3059" name=GENERATOR></HEAD>
<BODY>
<DIV dir=ltr align=left><FONT face=Arial color=#0000ff size=2><SPAN
class=035294315-16032007>BTW, how was it that my pauseProducing ever got invoked
below? after all I registered my producer (erroneously, no doubt
since it implements IPushProducer) as a pull producer by using the 2nd argument
of 'False' below:</SPAN></FONT></DIV>
<DIV dir=ltr align=left><FONT face=Arial color=#0000ff size=2><SPAN
class=035294315-16032007></SPAN></FONT> </DIV>
<DIV dir=ltr align=left><FONT face=Arial size=2><SPAN
class=035294315-16032007><FONT
face="Courier New">self.consumer.registerProducer(self, False)</FONT><FONT
face="Times New Roman" size=3> </FONT></SPAN></FONT></DIV>
<DIV><FONT face=Arial color=#0000ff size=2></FONT> </DIV>
<DIV><FONT face=Arial><FONT color=#0000ff><FONT size=2><SPAN
class=035294315-16032007>If indeed it's registered as a pull producer I wouldn't
think its pause should ever be called. Does twisted actually use the
type of the class to see what to call? Or getattr(class,'pauseProducing')
or somesuch? If so, what's the purpose of 'True' or 'False' during
registration?</SPAN></FONT></FONT></FONT></DIV>
<DIV><FONT face=Arial><FONT color=#0000ff><FONT size=2><SPAN
class=035294315-16032007></SPAN></FONT></FONT></FONT> </DIV>
<DIV><FONT face=Arial><FONT color=#0000ff><FONT size=2><SPAN
class=035294315-16032007>from the docs:</SPAN></FONT></FONT></FONT></DIV>
<BLOCKQUOTE dir=ltr style="MARGIN-RIGHT: 0px">
<DIV><FONT face=Arial><FONT><FONT size=2><SPAN class=035294315-16032007>
<H3><FONT color=#0000ff>registerProducer(producer, streaming)</FONT><A
name=auto8></A></H3>
<P><FONT color=#0000ff>So that a consumer can invoke methods on a producer,
the consumer needs to be told about the producer. This is done with the <CODE
class=python>registerProducer</CODE> method. The first argument is either a
<CODE class=python>IPullProducer</CODE> or <CODE
class=python>IPushProducer</CODE> provider; the second argument indicates
which of these interfaces is provided: <CODE class=python>True</CODE> for push
producers, <CODE class=python>False</CODE> for pull
producers.</FONT></P></DIV></BLOCKQUOTE>
<P dir=ltr><SPAN class=035294315-16032007><FONT
color=#0000ff>Thanks!</FONT></SPAN></SPAN></FONT></FONT></FONT></P>
<BLOCKQUOTE dir=ltr style="MARGIN-RIGHT: 0px">
<DIV class=OutlookMessageHeader lang=en-us dir=ltr align=left>
<HR tabIndex=-1>
</DIV>
<DIV class=OutlookMessageHeader lang=en-us dir=ltr align=left><FONT
face=Tahoma size=2><B>From:</B> twisted-python-bounces@twistedmatrix.com
[mailto:twisted-python-bounces@twistedmatrix.com] <B>On Behalf Of </B>Rutt,
Benjamin<BR><B>Sent:</B> Tuesday, March 06, 2007 12:04 PM<BR><B>To:</B>
twisted-python@twistedmatrix.com<BR><B>Subject:</B> [Twisted-Python] Learning
about IPushProducer<BR></FONT><BR></DIV>
<DIV></DIV><!-- Converted from text/rtf format -->
<P><FONT face="Courier New" size=2>When running the following code (my 2nd
twisted program!), it works as I had hoped - it doesn't starve any clients
that want to receive data back, even with a simultaneously active really long
streaming server-to-client communication (i.e. one piggy client asking for
millions of bytes). i.e. another client can get in and ask for just a
few bytes while a large payload is being delivered to a different
client. Which is great!</FONT></P>
<P><FONT face="Courier New" size=2>Here's a sample interaction from the client
side:</FONT> </P>
<P><FONT face="Courier New" size=2>$ telnet localhost 8007</FONT> <BR><FONT
face="Courier New" size=2>Trying 127.0.0.1...</FONT> <BR><FONT
face="Courier New" size=2>Connected to localhost.</FONT> <BR><FONT
face="Courier New" size=2>Escape character is '^]'.</FONT> <BR><FONT
face="Courier New" size=2>1</FONT> <BR><FONT face="Courier New"
size=2>x</FONT> <BR><FONT face="Courier New" size=2>2</FONT> <BR><FONT
face="Courier New" size=2>xx</FONT> <BR><FONT face="Courier New"
size=2>3</FONT> <BR><FONT face="Courier New" size=2>xxx</FONT> <BR><FONT
face="Courier New" size=2>10</FONT> <BR><FONT face="Courier New"
size=2>xxxxxxxxxx</FONT> <BR><FONT face="Courier New" size=2>99999</FONT>
<BR><FONT face="Courier New"
size=2>xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx</FONT></P>
<P><FONT face="Courier New" size=2>[...lots of x's...]</FONT> <BR><FONT
face="Courier New" size=2>xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx</FONT> <BR><FONT
face="Courier New" size=2>bye</FONT> <BR><FONT face="Courier New"
size=2>Connection closed by foreign host.</FONT> <BR><FONT face="Courier New"
size=2>$ </FONT></P>
<P><FONT face="Courier New" size=2>So I have 2 questions on my code:</FONT>
</P>
<P><FONT face="Courier New" size=2>1) am I doing anything wrong in setting up
the plumbing?</FONT> <BR><FONT face="Courier New" size=2>2) does
pauseProducing() get called by another thread whilst resumeProducing() is
running? (I believe it must, otherwise my resumeProducing() would only
be entered once). If so I should have an appropriate mutex around the
read/write of self.pause, no?</FONT></P>
<P><FONT face="Courier New" size=2>Here is the code, and output from the
server is at the end. Thanks -- Benjamin</FONT> </P>
<P><FONT face="Courier New" size=2>#!/usr/bin/env python</FONT> <BR><FONT
face="Courier New" size=2>import os, os.path, sys, re, commands, pickle,
tempfile, getopt, datetime</FONT> <BR><FONT face="Courier New" size=2>import
socket, string, random, time, traceback, shutil, popen2</FONT> </P>
<P><FONT face="Courier New" size=2>from zope.interface import
implements</FONT> <BR><FONT face="Courier New" size=2>from twisted.internet
import protocol, defer, interfaces, error, reactor</FONT> <BR><FONT
face="Courier New" size=2>from twisted.internet.protocol import Protocol,
Factory</FONT> <BR><FONT face="Courier New" size=2>from
twisted.protocols.basic import LineReceiver</FONT> </P>
<P><FONT face="Courier New" size=2>class NonStarvingXGiver:</FONT> <BR><FONT
face="Courier New" size=2>
implements(interfaces.IPushProducer)</FONT> <BR><FONT face="Courier New"
size=2> def __init__(self, howmany, consumer):</FONT>
<BR><FONT face="Courier New" size=2>
self.howmany = howmany</FONT> <BR><FONT face="Courier New"
size=2> self.sent_already = 0</FONT>
<BR><FONT face="Courier New" size=2>
self.paused = False</FONT> <BR><FONT face="Courier New"
size=2> self.consumer =
consumer</FONT> <BR><FONT face="Courier New" size=2> def
beginSendingXs(self):</FONT> <BR><FONT face="Courier New"
size=2> self.deferred = deferred =
defer.Deferred()</FONT> <BR><FONT face="Courier New"
size=2>
self.consumer.registerProducer(self, False)</FONT> <BR><FONT
face="Courier New" size=2> return
deferred</FONT> <BR><FONT face="Courier New" size=2> def
pauseProducing(self):</FONT> <BR><FONT face="Courier New"
size=2> print 'pauseProducing:
invoked'</FONT> <BR><FONT face="Courier New"
size=2> self.paused = True</FONT>
<BR><FONT face="Courier New" size=2> def
resumeProducing(self):</FONT> <BR><FONT face="Courier New"
size=2> print 'resumeProducing:
invoked'</FONT> <BR><FONT face="Courier New"
size=2> self.paused = False</FONT>
<BR><FONT face="Courier New" size=2>
maxchunksz = 1024</FONT> <BR><FONT face="Courier New"
size=2> while not self.paused and
self.howmany > self.sent_already:</FONT> <BR><FONT face="Courier New"
size=2>
chunksz = min(maxchunksz, self.howmany - self.sent_already)</FONT> <BR><FONT
face="Courier New"
size=2>
self.consumer.write('x' * chunksz)</FONT> <BR><FONT face="Courier New"
size=2>
self.sent_already += chunksz</FONT> <BR><FONT face="Courier New"
size=2> if self.howmany ==
self.sent_already:</FONT> <BR><FONT face="Courier New"
size=2>
self.consumer.write('\n')</FONT> <BR><FONT face="Courier New"
size=2>
self.consumer.unregisterProducer()</FONT> <BR><FONT face="Courier New"
size=2>
print 'resumeProducing: exiting for the last time'</FONT> <BR><FONT
face="Courier New" size=2> def stopProducing(self):</FONT>
<BR><FONT face="Courier New" size=2>
print 'stopProducing: invoked'</FONT> <BR><FONT face="Courier New"
size=2>
self.consumer.unregisterProducer()</FONT> <BR><FONT face="Courier New"
size=2> </FONT><BR><FONT
face="Courier New" size=2>class xgiver(LineReceiver):</FONT> <BR><FONT
face="Courier New" size=2> def lineReceived(self,
howmany):</FONT> <BR><FONT face="Courier New"
size=2> print 'got line [%s] from
client [%s]' % (howmany,</FONT> <BR><FONT face="Courier New"
size=2>
self.transport.getPeer())</FONT> <BR><FONT face="Courier New"
size=2> if howmany == 'bye':</FONT>
<BR><FONT face="Courier New"
size=2>
print 'goodbye to', self.transport.getPeer()</FONT> <BR><FONT
face="Courier New"
size=2>
self.transport.loseConnection()</FONT> <BR><FONT face="Courier New"
size=2>
return</FONT> <BR><FONT face="Courier New"
size=2> try:</FONT> <BR><FONT
face="Courier New"
size=2>
howmany = int(howmany)</FONT> <BR><FONT face="Courier New"
size=2> s =
NonStarvingXGiver(howmany, self.transport)</FONT> <BR><FONT face="Courier New"
size=2>
s.beginSendingXs()</FONT> <BR><FONT face="Courier New"
size=2> except Exception, ex:</FONT>
<BR><FONT face="Courier New"
size=2>
self.transport.write("invalid input " + howmany + "\n")</FONT> </P>
<P><FONT face="Courier New" size=2># Next lines are magic:</FONT> <BR><FONT
face="Courier New" size=2>factory = Factory()</FONT> <BR><FONT
face="Courier New" size=2>factory.protocol = xgiver</FONT> </P>
<P><FONT face="Courier New" size=2># 8007 is the port you want to run under.
Choose something >1024</FONT> <BR><FONT face="Courier New"
size=2>reactor.listenTCP(8007, factory)</FONT> <BR><FONT face="Courier New"
size=2>reactor.run()</FONT> </P><BR>
<P><FONT face="Courier New"
size=2>-------------------------------------------------------------------</FONT>
<BR><FONT face="Courier New" size=2>Server output:</FONT> </P>
<P><FONT face="Courier New" size=2>$ ./xgiver.py </FONT><BR><FONT
face="Courier New" size=2>got line [1] from client [IPv4Address(TCP,
'127.0.0.1', 51007)]</FONT> <BR><FONT face="Courier New"
size=2>resumeProducing: invoked</FONT> <BR><FONT face="Courier New"
size=2>resumeProducing: exiting for the last time</FONT> <BR><FONT
face="Courier New" size=2>got line [2] from client [IPv4Address(TCP,
'127.0.0.1', 51007)]</FONT> <BR><FONT face="Courier New"
size=2>resumeProducing: invoked</FONT> <BR><FONT face="Courier New"
size=2>resumeProducing: exiting for the last time</FONT> <BR><FONT
face="Courier New" size=2>got line [3] from client [IPv4Address(TCP,
'127.0.0.1', 51007)]</FONT> <BR><FONT face="Courier New"
size=2>resumeProducing: invoked</FONT> <BR><FONT face="Courier New"
size=2>resumeProducing: exiting for the last time</FONT> <BR><FONT
face="Courier New" size=2>got line [10] from client [IPv4Address(TCP,
'127.0.0.1', 51007)]</FONT> <BR><FONT face="Courier New"
size=2>resumeProducing: invoked</FONT> <BR><FONT face="Courier New"
size=2>resumeProducing: exiting for the last time</FONT> <BR><FONT
face="Courier New" size=2>got line [99999] from client [IPv4Address(TCP,
'127.0.0.1', 51007)]</FONT> <BR><FONT face="Courier New"
size=2>resumeProducing: invoked</FONT> <BR><FONT face="Courier New"
size=2>pauseProducing: invoked</FONT> <BR><FONT face="Courier New"
size=2>resumeProducing: invoked</FONT> <BR><FONT face="Courier New"
size=2>resumeProducing: exiting for the last time</FONT> <BR><FONT
face="Courier New" size=2>got line [bye] from client [IPv4Address(TCP,
'127.0.0.1', 51007)]</FONT> <BR><FONT face="Courier New" size=2>goodbye to
IPv4Address(TCP, '127.0.0.1', 51007)</FONT> </P></BLOCKQUOTE></BODY></HTML>