threaded mode

This commit is contained in:
Miguel Grinberg
2019-05-05 03:55:18 +00:00
parent ba986a89ff
commit 494800ff9f
3 changed files with 76 additions and 42 deletions

View File

@@ -1,3 +1,35 @@
try:
from sys import print_exception
except ImportError: # pragma: no cover
import traceback
def print_exception(exc):
traceback.print_exc()
concurrency_mode = 'threaded'
try: # pragma: no cover
import threading
def create_thread(f, *args, **kwargs):
"""Use the threading module."""
threading.Thread(target=f, args=args, kwargs=kwargs).start()
except ImportError: # pragma: no cover
try:
import _thread
def create_thread(f, *args, **kwargs):
"""Use MicroPython's _thread module."""
def run():
f(*args, **kwargs)
_thread.start_new_thread(run, ())
except ImportError:
def create_thread(f, *args, **kwargs):
"""No threads available, call function synchronously."""
f(*args, **kwargs)
concurrency_mode = 'sync'
try:
import ujson as json
except ImportError:
@@ -8,14 +40,6 @@ try:
except ImportError:
import re
try:
from sys import print_exception
except ImportError: # pragma: no cover
import traceback
def print_exception(exc):
traceback.print_exc()
try:
import usocket as socket
except ImportError:
@@ -268,6 +292,7 @@ class Microdot():
self.before_request_handlers = []
self.after_request_handlers = []
self.error_handlers = {}
self.debug = False
def route(self, url_pattern, methods=None):
def decorated(f):
@@ -291,33 +316,22 @@ class Microdot():
return decorated
def run(self, host='0.0.0.0', port=5000, debug=False):
self.debug = debug
s = socket.socket()
ai = socket.getaddrinfo(host, port)
addr = ai[0][-1]
if debug: # pragma: no cover
print('Listening on {host}:{port}...'.format(host=host, port=port))
if self.debug: # pragma: no cover
print('Starting {mode} server on {host}:{port}...'.format(
mode=concurrency_mode, host=host, port=port))
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(addr)
s.listen(5)
while True:
sock, addr = s.accept()
if not hasattr(sock, 'readline'): # pragma: no cover
stream = sock.makefile("rwb")
else:
stream = sock
req = Request.create(stream, addr)
res = self.dispatch_request(req)
if debug: # pragma: no cover
print('{method} {path} {status_code}'.format(
method=req.method, path=req.path,
status_code=res.status_code))
res.write(stream)
stream.close()
if stream != sock: # pragma: no cover
sock.close()
create_thread(self.dispatch_request, sock, addr)
def find_route(self, req):
f = None
@@ -329,7 +343,13 @@ class Microdot():
break
return f
def dispatch_request(self, req):
def dispatch_request(self, sock, addr):
if not hasattr(sock, 'readline'): # pragma: no cover
stream = sock.makefile("rwb")
else:
stream = sock
req = Request.create(stream, addr)
f = self.find_route(req)
try:
res = None
@@ -367,7 +387,14 @@ class Microdot():
res = Response(*res)
elif not isinstance(res, Response):
res = Response(res)
return res
res.write(stream)
stream.close()
if stream != sock: # pragma: no cover
sock.close()
if self.debug: # pragma: no cover
print('{method} {path} {status_code}'.format(
method=req.method, path=req.path,
status_code=res.status_code))
redirect = Response.redirect

View File

@@ -64,17 +64,11 @@ class Response(BaseResponse):
class Microdot(BaseMicrodot):
def run(self, host='0.0.0.0', port=5000, debug=False):
async def serve(reader, writer):
req = await Request.create(reader,
writer.get_extra_info('peername'))
res = await self.dispatch_request(req)
if debug: # pragma: no cover
print('{method} {path} {status_code}'.format(
method=req.method, path=req.path,
status_code=res.status_code))
self.debug = debug
async def serve(reader, writer):
if not hasattr(writer, 'awrite'): # pragma: no cover
# CPython adds the awrite and aclose methods in 3.8
# CPython provides the awrite and aclose methods in 3.8+
async def awrite(self, data):
self.write(data)
await self.drain()
@@ -87,17 +81,18 @@ class Microdot(BaseMicrodot):
writer.awrite = MethodType(awrite, writer)
writer.aclose = MethodType(aclose, writer)
await res.write(writer)
await writer.aclose()
await self.dispatch_request(reader, writer)
if debug: # pragma: no cover
print('Listening on {host}:{port}...'.format(host=host, port=port))
if self.debug: # pragma: no cover
print('Starting async server on {host}:{port}...'.format(
host=host, port=port))
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.start_server(serve, host, port))
loop.run_forever()
loop.close() # pragma: no cover
async def dispatch_request(self, req):
async def dispatch_request(self, reader, writer):
req = await Request.create(reader, writer.get_extra_info('peername'))
f = self.find_route(req)
try:
res = None
@@ -137,7 +132,12 @@ class Microdot(BaseMicrodot):
res = Response(*res)
elif not isinstance(res, Response):
res = Response(res)
return res
await res.write(writer)
await writer.aclose()
if self.debug: # pragma: no cover
print('{method} {path} {status_code}'.format(
method=req.method, path=req.path,
status_code=res.status_code))
async def _invoke_handler(self, f_or_coro, *args, **kwargs):
ret = f_or_coro(*args, **kwargs)

View File

@@ -4,15 +4,22 @@ from microdot import Microdot, Response
from tests import mock_socket
def mock_create_thread(f, *args, **kwargs):
f(*args, **kwargs)
class TestMicrodot(unittest.TestCase):
def setUp(self):
# mock socket module
self.original_socket = sys.modules['microdot'].socket
self.original_create_thread = sys.modules['microdot'].create_thread
sys.modules['microdot'].socket = mock_socket
sys.modules['microdot'].create_thread = mock_create_thread
def tearDown(self):
# restore original socket module
sys.modules['microdot'].socket = self.original_socket
sys.modules['microdot'].create_thread = self.original_create_thread
def test_get_request(self):
app = Microdot()