threaded mode
This commit is contained in:
81
microdot.py
81
microdot.py
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user