Tonny Dourado (@tonnydourado)
import socket
from multiprocessing.dummy import Pool
from threading import current_thread
def dispatcher(host, port):
print(f"Thread: {current_thread()}, Host: '{host}', port: '{port}")
thread_pool = Pool(5)
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind((host, port))
sock.listen(5)
while True:
client, address = sock.accept()
client.settimeout(60)
thread_pool.apply_async(process_request, (client, address))
def process_request(client, address, size=1024):
print(f"Thread: {current_thread()}, Client: {client}, "
f"Address: {address}")
while True:
try:
data = client.recv(size)
if data:
print(f"Data: {data}")
client.send(data)
break
finally:
client.close()
if __name__ == '__main__':
import sys
dispatcher(host=sys.argv[1], port=int(sys.argv[2]))
import select
import socket
from queue import Queue, Empty
class EventLoop(object):
def __init__(self, host="localhost", port=5000):
self.host = host
self.port = port
self._server = None
self._inputs, self._outputs = [], []
self._queues = {}
def __call__(self):
if self._server is not None:
raise Exception("Can't start server twice!")
self._start_listening()
self._inputs.append(self._server)
while self._inputs:
readable, writable, errors = select.select(
self._inputs,
self._outputs,
self._inputs
)
for sock in readable:
self._handle_readable(sock)
for sock in writable:
self._handle_writable(sock)
for sock in errors:
self._handle_error(sock)
def _start_listening(self):
self._server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self._server.setblocking(0)
self._server.bind((self.host, self.port))
self._server.listen(5)
def _handle_readable(self, sock):
# Quando a conexão de servidor está pronta pra ser lida, significa que
# temos um novo cliente:
if sock is self._server:
conn, addr = sock.accept()
conn.setblocking(0)
self._inputs.append(conn)
self._queues[conn] = Queue()
else:
data = sock.recv(1024)
if data:
# Colocamos os dados lidos na fila, pra enviar quando o socket
# estiver pronto para ser escrito:
self._queues[sock].put(data)
# Caso seja uma nova conexão, vamos monitorar ela para escrita
# também:
if sock not in self._outputs:
self._outputs.append(sock)
else:
# Sem dados, podemos fechar a conexão:
if sock in self._outputs:
self._outputs.remove(sock)
self._inputs.remove(sock)
del self._queues[sock]
sock.close()
def _handle_writable(self, sock):
try:
next_msg = self._queues[sock].get_nowait()
except Empty:
self._outputs.remove(sock)
self._inputs.remove(sock)
del self._queues[sock]
sock.close()
else:
sock.send(next_msg)
def _handle_error(self, sock):
self._inputs.remove(sock)
if sock in self._outputs:
self._outputs.remove(sock)
del self._queues[sock]
sock.close()
if __name__ == '__main__':
import sys
loop = EventLoop(host=sys.argv[1], port=int(sys.argv[2]))
loop()
import json
def connectionError(failure):
failure.trap(ConnectionError)
log.failure("makeRequest failed due to connection error", failure)
return []
def getUsers():
d = makeRequest("GET", "/users")
d.addCallback(json.loads)
d.addErrback(connectionError)
return d
from twisted.internet import reactor, defer
def _triggerDeferred(d, x):
if x % 2 == 0:
d.callback(x * 3)
else:
d.errback(ValueError("You used an odd number!"))
def getDeferred(x):
d = defer.Deferred()
# Fingir que demorou pra rodar:
reactor.callLater(2, _triggerDeferred, d, x)
d.addCallback(firstCallback)
return d
def firstCallback(result):
print("First callback!")
return f"Result: {result}"
def cbPrintData(result):
print("Yay!")
print(result)
def ebPrintError(failure):
print("Oh, no!")
print(repr(failure))
def main():
# Vai imprimir a mensagem de erro que passamos pra ValueError
d = getDeferred(3)
d.addCallback(cbPrintData)
d.addErrback(ebPrintError)
# Vai imprimir "Result: 12"
d = getDeferred(4)
d.addCallback(cbPrintData)
d.addErrback(ebPrintError)
reactor.callLater(4, reactor.stop)
reactor.run()
if __name__ == "__main__":
main()
Dados
Deferred
Result ou
Failure
Result
Failure
Callbacks
Errbacks
import json
def connectionError(failure):
failure.trap(ConnectionError)
log.failure("makeRequest failed due to connection error", failure)
return []
def getUsers():
d = makeRequest("GET", "/users")
d.addCallback(json.loads)
d.addErrback(connectionError)
return d
import json
def connectionError(failure):
failure.trap(ConnectionError)
log.failure("makeRequest failed due to "
"connection error")
return []
def getUsers():
d = makeRequest("GET", "/users")
d.addCallback(json.loads)
d.addErrback(connectionError)
return d
import json
from twisted.internet.defer import (
inlineCallbacks, returnValue
)
@inlineCallbacks
def getUsers(self):
try:
response = yield makeRequest("GET", "/users")
except ConnectionError:
log.failure("makeRequest failed due to "
"connection error")
returnValue([])
returnValue(json.loads(response))
def my_range(n):
i = 0
while i < n:
yield i
i += 1
g = my_range(3)
print(next(g)) # Output: 0
print(next(g)) # Output: 1
print(next(g)) # Output: 2
print(next(g)) # Raise StopIteration
for i in my_range(10):
print(i, end=" ")
print()
# Output: 0 1 2 3 4 5 6 7 8 9
def hello():
person = yield
greeting = f"Hello, {person}!!!"
yield greeting
def say_hello(name):
hw = hello()
next(hw)
greeting = hw.send(name)
print(greeting)
say_hello("Tonny")
# Output: 'Hello, Tonny!!!'
import json
from twisted.internet.defer import (
inlineCallbacks, returnValue
)
@inlineCallbacks
def getUsers(self):
try:
response = yield makeRequest("GET", "/users")
except ConnectionError:
log.failure("makeRequest failed due to "
"connection error")
returnValue([])
returnValue(json.loads(response))
import json
from twisted.internet.defer import ensureDeferred
from twisted.logger import Logger
log = Logger()
async def getUsers():
try:
response = await makeRequest("GET", "/users")
return json.loads(response)
except ConnectionError:
log.failure("makeRequest failed due to"
" connection error")
return []
def do():
d = ensureDeferred(getUsers())
d.addCallback(print)
return d
async/await:
__await__, send, throw, close
asyncio:
Pluggable event loop
Transports, protocols
Futures
import asyncio
from datetime import datetime
from twisted.internet.task import react, LoopingCall
from twisted.internet.defer import ensureDeferred, Deferred
from twisted.internet import asyncioreactor
asyncioreactor.install(asyncio.get_event_loop())
def sleep(secs):
coroutine = asyncio.sleep(secs)
future = asyncio.ensure_future(coroutine)
return Deferred.fromFuture(future)
async def start_request():
urls = [
'https://example.com/page/1',
'https://example.com/page/2',
'https://example.com/page/3'
]
for url in urls:
print(f"Downloading page '{url}'", end="...\n")
await sleep(1)
print(f"Page '{url}' download finished")
async def after_request():
await sleep(2)
print("Downloaded all pages!")
def main(reactor):
counter = LoopingCall(lambda: print(datetime.now()))
counter.start(0.5)
deferred = ensureDeferred(start_request())
deferred.addCallback(lambda r: ensureDeferred(after_request()))
deferred.addErrback(print)
return deferred
if __name__ == "__main__":
react(main)
2018-04-04 17:05:05.060931
Downloading page 'https://example.com/page/1'...
2018-04-04 17:05:05.561611
2018-04-04 17:05:06.062427
Page 'https://example.com/page/1' download finished
Downloading page 'https://example.com/page/2'...
2018-04-04 17:05:06.562397
2018-04-04 17:05:07.062301
Page 'https://example.com/page/2' download finished
Downloading page 'https://example.com/page/3'...
2018-04-04 17:05:07.561850
2018-04-04 17:05:08.061805
Page 'https://example.com/page/3' download finished
2018-04-04 17:05:08.562057
2018-04-04 17:05:09.061871
2018-04-04 17:05:09.561685
2018-04-04 17:05:10.062463
Downloaded all pages!