summaryrefslogtreecommitdiff
path: root/doc/historic/2003/pycon/twisted-internet/twisted-internet.py
diff options
context:
space:
mode:
Diffstat (limited to 'doc/historic/2003/pycon/twisted-internet/twisted-internet.py')
-rw-r--r--doc/historic/2003/pycon/twisted-internet/twisted-internet.py541
1 files changed, 541 insertions, 0 deletions
diff --git a/doc/historic/2003/pycon/twisted-internet/twisted-internet.py b/doc/historic/2003/pycon/twisted-internet/twisted-internet.py
new file mode 100644
index 0000000..1948d3e
--- /dev/null
+++ b/doc/historic/2003/pycon/twisted-internet/twisted-internet.py
@@ -0,0 +1,541 @@
+#!/usr/bin/python
+
+from slides import Lecture, Slide, Image, Bullet, PRE, URL, SubBullet, NumSlide, toHTML
+import os
+
+class Bad:
+ """Marks the text in red."""
+
+ def __init__(self, text):
+ self.text = text
+
+ def toHTML(self):
+ return '<font color="red">%s</font>' % toHTML(self.text)
+
+
+class Lecture(Lecture):
+
+ def getFooter(self):
+ return '<div class="footer"><hr noshade />Presented by <b>ZOTECA&nbsp;</b></div>'
+
+
+EVENT_LOOP_CODE = """\
+# pseudo-code reactor
+class Reactor:
+ def run(self):
+ while 1:
+ e = self.getNextEvent()
+ e.run()
+"""
+
+PROTOCOL_CODE = """\
+from twisted.internet.protocol import Protocol
+
+class Echo(Protocol):
+ def connectionMade(self):
+ print 'connection made with', self.transport.getPeer()
+ def dataReceived(self, data):
+ self.transport.write(data)
+ def connectionLost(self, reason):
+ print 'connection was lost, alas'
+"""
+
+SERVER_CODE = """\
+from twisted.internet.protocol import ServerFactory
+
+class EchoFactory(ServerFactory):
+
+ def buildProtocol(self, addr):
+ p = Echo()
+ p.factory = self
+ return p
+"""
+
+RUNNING_SERVER_CODE = """\
+from twisted.internet import reactor
+
+f = EchoFactory()
+reactor.listenTCP(7771, f)
+reactor.run()
+"""
+
+CLIENT_PROTOCOL_CODE = """\
+from twisted.internet.protocol import Protocol
+
+class MyClientProtocol(Protocol):
+ buffer = ''
+ def connectionMade(self):
+ self.transport.write('hello world')
+ def dataReceived(self, data):
+ self.buffer += data
+ if self.buffer == 'hello world':
+ self.transport.loseConnection()
+"""
+
+CLIENT_FACTORY_CODE = """\
+from twisted.internet.protocol import ClientFactory
+
+class MyFactory(ClientFactory):
+
+ protocol = MyClientProtocol
+
+ def startedConnecting(self, connector):
+ pass # we could connector.stopConnecting()
+ def clientConnectionMade(self, connector):
+ pass # we could connector.stopConnecting()
+ def clientConnectionLost(self, connector, reason):
+ connector.connect() # reconnect
+ def clientConnectionFailed(self, connector, reason):
+ print "connection failed"
+ reactor.stop()
+"""
+
+CLIENT_CONNECT_CODE = """\
+from twisted.internet import reactor
+
+reactor.connectTCP('localhost', 7771, MyFactory(), timeout=30)
+reactor.run()
+"""
+
+PULL_PRODUCER_CODE = """\
+class FileProducer:
+
+ def __init__(self, file, size, transport):
+ self.file = file; self.size = size
+ self.transport = transport # the consumer
+ transport.registerProducer(self, 0)
+
+ def resumeProducing(self):
+ if not self.transport: return
+ self.transport.write(self.file.read(16384))
+ if self.file.tell() == self.size:
+ self.transport.unregisterProducer()
+ self.transport = None
+
+ def pauseProducing(self): pass
+
+ def stopProducing(self):
+ self.file.close()
+ self.request = None
+"""
+
+PUSH_PRODUCER_CODE = """\
+from twisted.internet import reactor
+
+class GarbageProducer:
+
+ def __init__(self, transport):
+ self.paused = 0; self.stopped = 0
+ self.transport = transport
+ transport.registerProducer(self, 1)
+ self.produce()
+
+ def produce(self):
+ if not self.paused:
+ self.transport.write('blabla')
+ if not self.stopped:
+ reactor.callLater(0.1, self.produce)
+
+ def stopProducing(self):
+ self.stopped = 1
+
+ def pauseProducing(self):
+ self.paused = 1
+
+ def resumeProducing(self):
+ self.paused = 0
+"""
+
+SCHEDULING_CODE = """\
+from twisted.internet import reactor
+
+def f(x, y=1):
+ print x, y
+
+i = reactor.callLater(0.1, f, 2, y=4)
+i.delay(2)
+i.reset(1)
+i.cancel()
+"""
+
+FACTORY_START_CODE = """\
+from twisted.internet.protocol import ServerFactory
+
+class LogFactory(ServerFactory):
+
+ def startFactory(self):
+ self.log = open('log.txt', 'w')
+
+ def stopFactory(self):
+ self.log.close()
+"""
+
+LOGGING_CODE = """\
+from twisted.python import log
+
+# by default only errors are logged, to stderr
+logFile = open('log.txt', 'a')
+log.startLogging(logFile)
+
+log.msg('Something has occurred')
+"""
+
+LOGGING_ERRORS_CODE = """
+from twisted.python import log, failure
+
+e = ValueError('ONO')
+log.err(failure.Failure(e))
+
+try:
+ doSomethingElse()
+except:
+ log.deferr()
+"""
+
+SERVICE_CODE = """\
+from twisted.internet import app
+
+class FooService(app.ApplicationService):
+ def startService(self):
+ # do startup stuff
+ def stopService(self):
+ # do shutdown stuff
+ def foobrizate(self):
+ # business logic!
+
+application = app.Application('foobnator')
+svc = FooService('foo', application)
+application.getServiceNamed('foo') is svc # True
+"""
+
+RUNNABLE_APP_CODE = """\
+# this is web.py
+from twisted.internet import app
+from twisted.web import static, server
+
+application = app.Application('web')
+application.listenTCP(8080, server.Site(static.File('/var/www')))
+
+if __name__ == '__main__':
+ application.run(save=0)
+"""
+
+TWISTD_CODE = """\
+$ twistd -y web.py
+$ lynx http://localhost:8080
+$ kill `cat twistd.pid`
+"""
+
+GUI_CODE = """\
+from twisted.internet import gtkreactor
+gtkreactor.install()
+import gtk
+w = gtk.GtkWindow(gtk.WINDOW_TOPLEVEL)
+w.show_all()
+from twisted.internet import reactor
+reactor.run()
+"""
+
+lecture = Lecture(
+ "The twisted.internet Tutorial of Doom",
+
+ Slide("Part 1 - Introduction"),
+
+ # there are different ways to do networking
+ # mention processes are not cross-platform
+ Slide("Choosing a networking paradigm for the enterprise",
+ Bullet("Event driven"),
+ Bullet(Bad("Threads")),
+ Bullet("Others which we will ignore (processes, SEDA, ...)")),
+
+ # it's a metaphor!
+ Slide("Applied Bistromathics 101",
+ Bullet("Consider a restaurant as a network application"),
+ Bullet("Clients come in, make requests to the waiters"),
+ Bullet("Waiters act on clients' choices")),
+
+ # an event loop is efficient, doesn't waste time
+ # event loop is also used for GUIs
+ Slide("The event driven waiter",
+ Bullet("One waiter, serving all tables"),
+ Bullet("Waiter takes orders from tables to kitchen"),
+ Bullet("Waiter takes food from kitchen to tables")),
+
+ # not accurate, but the problems are real. avoid threads if you can
+ Slide("Threads (a caricature)",
+ Bullet(Bad("One waiter per table")),
+ SubBullet("Problems:",
+ Bullet(Bad("Expensive")),
+ Bullet(Bad("Waiters need to be careful not bump into each other")),
+ )),
+
+ # why threads are sometimes necessary
+ Slide("When do we want threads?",
+ Bullet("Long running, blocking operations"),
+ Bullet("Classic example: database access")),
+
+ # today we will discuss only (parts of) twisted.internet
+ Slide("Twisted: The Framework of Your Internet",
+ Image("twisted-overview.png")),
+
+ Slide("Project Stats",
+ Bullet("URL: ", URL("http://www.twistedmatrix.com")),
+ Bullet("License: LGPL"),
+ Bullet("Number of developers: approximately 20"),
+ Bullet("Version: 1.0.3"),
+ Bullet("Platforms: Unix, Win32"),
+ Bullet("Started in January 2000 by Glyph Lefkowitz")),
+
+ Slide("Part 2 - Basic Networking With Twisted"),
+
+ # quick review of how the internet works
+ Slide("Internet!",
+ Bullet("Network of interconnected machines"),
+ Bullet("Each machine has one (or more) IP addresses"),
+ Bullet("DNS maps names ('www.yahoo.com') to IPs (216.109.125.69)"),
+ Bullet("TCP runs on top of IP, servers listen on of of 65536 ports,"
+ " e.g. HTTP on port 80"),),
+
+ # we need to understand certain basic terms before we continue.
+ # the event loop is the last thing we run - it waits until
+ # an event occurs, then calls the appropriate handler.
+ Slide("Basic Definitions - Reactor",
+ Bullet("An object implementing the event loop",
+ PRE(EVENT_LOOP_CODE))),
+
+ Slide("Basic Definitions - Transport",
+ Bullet("Moves data from one location to another"),
+ Bullet("Main focus of talk are ordered, reliable byte stream transports"),
+ Bullet("Examples: TCP, SSL, Unix sockets"),
+ Bullet("UDP is a different kind of transport")),
+
+ # the client is the side which initiated the connection
+ # HTTP and SSH run on TCP-like transports, DNS runs on UDP or TCP
+ Slide("Basic Definitions - Protocol",
+ Bullet("Defines the rules for communication between two hosts"),
+ Bullet("Protocols communicate using a transport"),
+ Bullet("Typically there is a client, and server"),
+ Bullet("Examples: HTTP, SSH, DNS")),
+
+ Slide("All Together Now",
+ Bullet("The reactor gets events from the transports (read from network, write to network)"),
+ Bullet("The reactor passes events to protocol (connection lost, data received)"),
+ Bullet("The protocol tells the transport to do stuff (write data, lose connection)")),
+
+ # designing a new protocol is usually a bad idea, there are lots of
+ # things you can get wrong, both in design and in implementation
+ Slide("How To Implement A Protocol",
+ Bullet("Hopefully, you don't.")),
+
+ # XXX split into three expanded slides?
+ NumSlide("How To Not Implement A Protocol",
+ Bullet("Use an existing Twisted implementation of the protocol"),
+ Bullet("Use XML-RPC"),
+ Bullet("Use Perspective Broker, a remote object protocol")),
+
+ # connectionMade is called when connection is made
+ # dataReceived is called every time we receive data from the network
+ # connectionLost is called when the connection is lost
+ Slide("How To Really Implement A Protocol",
+ PRE(PROTOCOL_CODE)),
+
+ # factories - why?
+ Slide("Factories",
+ Bullet("A protocol instance only exists as long as the connection is there"),
+ Bullet("Protocols want to share state"),
+ Bullet("Solution: a factory object that creates protocol instances")),
+
+ # factory code - notice how protocol instances have access to the factory
+ # instance, for shared state. buildProtocol can return None if we don't
+ # want to accept connections from that address.
+ Slide("A Server Factory",
+ PRE(SERVER_CODE)),
+
+ # running the server we just wrote
+ Slide("Connecting A Factory To A TCP Port",
+ PRE(RUNNING_SERVER_CODE)),
+
+ # transport independence - using listenUNIX as example
+ Slide("Transport Independence",
+ Bullet("Notice how none of the protocol code was TCP specific"),
+ Bullet("We can reuse same protocol with different transports"),
+ Bullet("We could use listenUNIX for unix sockets with same code"),
+ Bullet("Likewise listenSSL for SSL or TLS")),
+
+ Slide("Client Side Protocol",
+ PRE(CLIENT_PROTOCOL_CODE)),
+
+ # client connections are different
+ Slide("Client Side Factories",
+ Bullet("Different requirements than server"),
+ Bullet("Failure to connect"),
+ Bullet("Automatic reconnecting"),
+ Bullet("Cancelling and timing out connections")),
+
+ # example client factory - explain use of default buildProtocol
+ Slide("Client Side Factories 2",
+ PRE(CLIENT_FACTORY_CODE)),
+
+ # connectTCP
+ Slide("Connection API",
+ PRE(CLIENT_CONNECT_CODE)),
+
+ # explain how transports buffer the output
+ Slide("Buffering",
+ Bullet("When we write to transport, data is buffered"),
+ Bullet("loseConnection will wait until all buffered data is sent, and producer (if any) is finished")),
+
+ # start/stopFactory
+ Slide("Factory Resources",
+ Bullet("Factories may want to create/clean up resources"),
+ Bullet("startFactory() - called on start of listening/connect"),
+ Bullet("stopFactory() - called on end of listening/connect"),
+ Bullet("Called once even if factory listening/connecting multiple ports")),
+
+ # example of restartable factory
+ Slide("Factory Resources 2",
+ PRE(FACTORY_START_CODE)),
+
+ Slide("Producers and Consumers",
+ Bullet("What if we want to send out lots of data?"),
+ Bullet("Can't write it out all at once"),
+ Bullet("We don't want to write too fast")),
+
+ Slide("Producers",
+ Bullet("Produce data for a consumer, in this case by calling transport's write()"),
+ Bullet("Pausable (should implement pauseProducing and resumeProducing methods)"),
+ Bullet("Push - keeps producing unless told to pause"),
+ Bullet("Pull - produces only when consumer tells it to")),
+
+ Slide("Consumers",
+ Bullet("registerProducer(producer, streaming)"),
+ Bullet("Will notify producer to pause if buffers are full")),
+
+ Slide("Sample Pull Producer",
+ PRE(PULL_PRODUCER_CODE)),
+
+ Slide("Sample Push Producer",
+ PRE(PUSH_PRODUCER_CODE)),
+
+ # scheduling events
+ Slide("Scheduling",
+ PRE(SCHEDULING_CODE)),
+
+ # pluggable reactors - why?
+ Slide("Choosing a Reactor - Why?",
+ Bullet("GUI toolkits have their own event loop"),
+ Bullet("Platform specific event loops")),
+
+ Slide("Choosing a Reactor",
+ Bullet("Twisted supports multiple reactors"),
+ Bullet("Default, gtk, gtk2, qt, win32 and others"),
+ Bullet("Tk and wxPython as non-reactors"),
+ Bullet("Reactor installation should be first thing code does")),
+
+ # example GUI client
+ Slide("Example GTK Program",
+ PRE(GUI_CODE)),
+
+ # you can learn more about
+ Slide("Learning more about networking and scheduling",
+ Bullet("twisted.internet.interfaces"),
+ Bullet("http://twistedmatrix.com/document/howtos/")),
+
+
+ Slide("Part 3 - Building Applications With Twisted"),
+
+ # the concept of the application
+ Slide("Applications",
+ Bullet("Reactor is a concept of event loop"),
+ Bullet("Application is higher-level"),
+ Bullet("Configuration, services, persistence"),
+ Bullet("Like reactor, you can listenTCP, connectTCP, etc.")),
+
+ # services concept
+ Slide("Services",
+ Bullet("Services can be registered with Application"),
+ Bullet("A service encapsulates 'business logic'"),
+ Bullet("Infrastructure outside the scope of protocols"),
+ Bullet("Examples: authentication, mail storage")),
+
+ # service example code
+ Slide("Services 2",
+ PRE(SERVICE_CODE)),
+
+ # logging
+ Slide("Logging",
+ PRE(LOGGING_CODE)),
+
+ # logging errors
+ # explain why this is good idea (twistd -b)
+ Slide("Logging Errors",
+ PRE(LOGGING_ERRORS_CODE)),
+
+ # twistd idea
+ Slide("twistd - Application Runner",
+ Bullet("Single access point for running applications"),
+ Bullet("Separate configuration from deployment")),
+
+ # twistd features
+ Slide("twistd Features",
+ Bullet("Daemonization"),
+ Bullet("Log file selection (including to syslog)"),
+ Bullet("Choosing reactor"),
+ Bullet("Running under debugger"),
+ Bullet("Profiling"),
+ Bullet("uid, gid"),
+ Bullet("Future: WinNT Services")),
+
+ # making modules for twistd -y
+ Slide("Making a runnable application",
+ PRE(RUNNABLE_APP_CODE)),
+
+ # running the server
+ Slide("Running twistd",
+ PRE(TWISTD_CODE)),
+
+ Slide("Part 4: Further Bits and Pieces"),
+
+ Slide("Other twisted.internet Features",
+ Bullet("UDP, Multicast, Unix sockets, Serial"),
+ Bullet("Thread integration")),
+
+ Slide("Deferreds",
+ Bullet("Deferred - a promise of a result"),
+ Bullet("Supports callback chains for results and exceptions"),
+ Bullet("Used across the whole framework"),
+ Bullet("Make event-driven programming much easier"),
+ Bullet("Can work with asyncore too, not just Twisted")),
+
+ Slide("Protocol implementations",
+ Bullet("Low-level implementations, without policies"),
+ Bullet("SSH, HTTP, SMTP, IRC, POP3, telnet, FTP, TOC, OSCAR, SOCKSv4, finger, DNS, NNTP, IMAP, LDAP"),
+ Bullet("Common GPS modem protocols")),
+
+ Slide("Frameworks",
+ Bullet("twisted.web - Web server framework"),
+ Bullet("twisted.news - NNTP server framework"),
+ Bullet("twisted.words - messaging framework"),
+ Bullet("twisted.names - DNS server")),
+
+ Slide("Perspective Broker",
+ Bullet("Object publishing protocol"),
+ Bullet("Fast, efficient and extendable"),
+ Bullet("Two-way, asynchronous"),
+ Bullet("Secure and encourages secure model"),
+ Bullet("Implemented in Python for Twisted, and Java")),
+
+ Slide("Lore",
+ Bullet("Simple documentation system"),
+ Bullet("Simple subset of XHTML"),
+ Bullet("Generates LaTeX, XHTML")),
+
+ Slide("Reality",
+ Bullet("Multiplayer text simulation framework"),
+ Bullet("Original source of Twisted project"),
+ Bullet("Now a totally different project")),
+)
+
+
+if __name__ == '__main__':
+ lecture.renderHTML(".", "twisted_internet-%02d.html", css="main.css")