Twisted is an event-driven networking engine written in Python. It provides tools for developing asynchronous network applications including an asynchronous I/O core, Deferreds (promises and pipelines), and implementations of many common protocols like HTTP, FTP, SMTP, POP3, IMAP, SSH, DNS, IRC, NNTP, XMPP, and more. Twisted also includes frameworks for application development, services, plugins, remote procedure calls (RPC), threads, databases, and a web server.
9. from twisted.internet import reactor, protocol
from twisted.protocols.basic import LineReceiver
class MyChatProtocol(LineReceiver):
def connectionMade(self):
self.factory.clients.append(self)
def connectionLost(self, reason):
self.factory.clients.remove(self)
def lineReceived(self, line):
for c in self.factory.clients:
if c is not self:
c.sendLine(line)
class MyChatFactory(protocol.ServerFactory):
protocol = MyChatProtocol
def startFactory(self):
self.clients = []
reactor.listenTCP(1234, MyChatFactory())
reactor.run()
10. Transport – Protocol – Factory
Транспорт
прием-передача данных
tcp, udp, unix, ssl, process, loopback
Протокол
парсинг входящих данных
state уровня соединения
Фабрика
создание протоколов (reconnect etc)
state уровня приложения
11. from twisted.internet import reactor
from twisted.internet.protocol import Protocol, ClientCreator
class Greeter(Protocol):
def dataReceived(self, data):
print(data)
def send_message(self, msg):
self.transport.write("%srn" % msg)
def gotProtocol(g):
g.send_message("Hello!")
reactor.callLater(5, g.send_message, "How are you?")
reactor.callLater(20, g.send_message, "Bye.")
reactor.callLater(21, g.transport.loseConnection)
c = ClientCreator(reactor, Greeter)
c.connectTCP("localhost", 1234).addCallback(gotProtocol)
reactor.run()
12. Deferred
● Аналог Future/Promise + Pipeline
● В Deferred лежит:
– список пар (callback, errback)
– готовое значение / исключение
● Возможность приостановки
● Поддержка отмен (cancellation)
● Но нету таймаутов (раньше были)
● Печать errback в лог из __del__
13. import random
from twisted.internet import reactor, defer
def lowlevel_async_op(data, callback, errback):
if random.randint(0, 42):
reactor.callLater(3, callback, data)
else:
reactor.callLater(2, errback, Exception())
def do_request():
d = defer.Deferred()
lowlevel_async_op("123", d.callback, d.errback)
return d
def clean_data(data):
return int(data)
def process_data(data):
print("got %r" % data)
def log_error(f):
print("error: %s" % f)
d = do_request()
d.addCallback(clean_data)
d.addCallback(process_data)
d.addErrback(log_error)
d.addBoth(lambda _: reactor.stop())
reactor.run()
21. class IProtocol(zope.interface.Interface):
def dataReceived(data):
"""
Called whenever data is received.
"""
def connectionLost(reason):
"""
Called when the connection is shut down.
@type reason: L{twisted.python.failure.Failure}
"""
def makeConnection(transport):
"""
Make a connection to a transport and a server.
"""
def connectionMade():
"""
Called when a connection is made.
"""
28. Application framework
class IService(Interface):
def setServiceParent(parent):
""" Set the parent of the service. """
def disownServiceParent():
"""Remove L{IService} from L{IServiceCollection}."""
def privilegedStartService():
""" Do preparation work for starting the service."""
def startService():
""" Start the service. """
def stopService():
""" Stop the service. """
33. twistd plugins
class IServiceMaker(Interface):
tapname = Attribute("A short string naming this Twisted plugin")
description = Attribute("A brief summary of the features")
options = Attribute("A C{twisted.python.usage.Options} subclass")
def makeService(options):
"""
Create and return an object providing
L{twisted.application.service.IService}.
"""
~> twistd [opts] <<tapname>> … [plugin-opts]
~> twistd -l bs.log procmon buggy-script.py
Например
34. Spread – perspective broker
● Удаленный вызов методов (RPC)
● Асинхронный
● Симметричный
● Своя сериализация (jelly / banana)
● Передача объектов как по ссылке
(Referencable) так и значению (Copyable)
● Передача больших объектов (Cacheable)
35. from twisted.spread import pb
from twisted.application import service, internet
class Formatter(pb.Referenceable):
def __init__(self, format_spec):
self.format_spec = format_spec
def remote_formatIt(self, value):
return format(value, self.format_spec)
class ServerObject(pb.Root):
def remote_getFormatter(self, format_spec):
return Formatter(format_spec)
application = service.Application("pb-server")
sobj = ServerObject()
bs = internet.TCPServer(8800, pb.PBServerFactory(sobj))
bs.setServiceParent(application)
36. from twisted.internet import defer, task
from twisted.spread import pb
@defer.inlineCallbacks
def main(reactor):
cf = pb.PBClientFactory()
reactor.connectTCP("localhost", 8800, cf)
root = yield cf.getRootObject()
fmt = yield root.callRemote('getFormatter', ".2f")
res = yield fmt.callRemote('formatIt', 1.2345)
print(res)
if __name__ == '__main__':
task.react(main)
37. Библиотеки
websocket, memcache, redis, riak,
couchdb, cassandra, postgresql,
amqp, stomp, solr, xmpp, oscar, msn,
snmp, smpp, ldap, webdav
…
любая асинхронная библиотека
с расширяемым реактором
38. Threads
class IReactorThreads(Interface):
def getThreadPool():
"Return the threadpool used by L{callInThread}."
def callInThread(callable, *args, **kwargs):
"Run the callable object in a separate thread."
def callFromThread(callable, *args, **kw):
"Cause a function to be executed by the reactor."
def suggestThreadPoolSize(size):
"Suggest the size of the internal threadpool."
Helpers:
● blockingCallFromThread(reactor, f, *a, **kw)
● deferToThread(f, *args, **kwargs)
● callMultipleInThread(tupleList)
39. adbapi
from twisted.enterprise import adbapi
from twisted.internet import defer, task
dbpool = adbapi.ConnectionPool('sqlite3',
"file.db", check_same_thread=False)
@defer.inlineCallbacks
def useless_work():
yield dbpool.runOperation(
"CREATE TABLE Users (name, nick)")
yield dbpool.runOperation(
"INSERT INTO Users (name, nick) VALUES (?, ?)",
["Andrei", "anjensan"])
nick = yield dbpool.runQuery(
"SELECT nick FROM Users WHERE name = ?",
["Andrei"])
print(nick)
48. import sys
from twisted.internet import defer, task
from twisted.web import template
from twisted.python.filepath import FilePath
class MyElement(template.Element):
loader = template.XMLFile(FilePath("template.xml"))
@template.renderer
def title(self, request, tag):
return tag("Title")
@template.renderer
def widgets(self, request, tag):
for wn in ["first", "second", "third"]:
yield task.deferLater(
reactor, 1,
lambda: tag.clone().fillSlots(widget_name=wn))
def main(reactor):
return template.flatten(None, MyElement(), sys.stdout.write)
task.react(main)
<p xmlns:t="http://twistedmatrix.com/ns/twisted.web.template/0.1">
<p t:render="title" />
<ul>
<li t:render="widgets">
<b><t:slot name="widget_name"/></b>
</li>
</ul>
</p>
Twisted web templates
49. Twisted projects
● Core – Asynchronous event loop and networking framework.
● Web – An HTTP protocol implementation.
● Conch – An SSH and SFTP protocol implementation.
● Mail – An SMTP, IMAP and POP protocol implementation.
● Names – A DNS protocol implementation with client and server.
● News – An NNTP protocol implementation with client and server.
● Words – Chat and Instant Messaging.
● Runner – Process management, including an inetd server.
● Pair – Low-level networking transports and utilities.
● Trial – Unittest-compatible automated testing.
50. In a Nutshell, Twisted...
has had 16,823 commits made by 86 contributors
representing 196,503 lines of code
took an estimated 51 years of effort (COCOMO model)
starting with its first commit in July, 2001
http://ohloh.net/p/twisted