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:
|
try:
|
||||||
import ujson as json
|
import ujson as json
|
||||||
except ImportError:
|
except ImportError:
|
||||||
@@ -8,14 +40,6 @@ try:
|
|||||||
except ImportError:
|
except ImportError:
|
||||||
import re
|
import re
|
||||||
|
|
||||||
try:
|
|
||||||
from sys import print_exception
|
|
||||||
except ImportError: # pragma: no cover
|
|
||||||
import traceback
|
|
||||||
|
|
||||||
def print_exception(exc):
|
|
||||||
traceback.print_exc()
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import usocket as socket
|
import usocket as socket
|
||||||
except ImportError:
|
except ImportError:
|
||||||
@@ -268,6 +292,7 @@ class Microdot():
|
|||||||
self.before_request_handlers = []
|
self.before_request_handlers = []
|
||||||
self.after_request_handlers = []
|
self.after_request_handlers = []
|
||||||
self.error_handlers = {}
|
self.error_handlers = {}
|
||||||
|
self.debug = False
|
||||||
|
|
||||||
def route(self, url_pattern, methods=None):
|
def route(self, url_pattern, methods=None):
|
||||||
def decorated(f):
|
def decorated(f):
|
||||||
@@ -291,33 +316,22 @@ class Microdot():
|
|||||||
return decorated
|
return decorated
|
||||||
|
|
||||||
def run(self, host='0.0.0.0', port=5000, debug=False):
|
def run(self, host='0.0.0.0', port=5000, debug=False):
|
||||||
|
self.debug = debug
|
||||||
|
|
||||||
s = socket.socket()
|
s = socket.socket()
|
||||||
ai = socket.getaddrinfo(host, port)
|
ai = socket.getaddrinfo(host, port)
|
||||||
addr = ai[0][-1]
|
addr = ai[0][-1]
|
||||||
|
|
||||||
if debug: # pragma: no cover
|
if self.debug: # pragma: no cover
|
||||||
print('Listening on {host}:{port}...'.format(host=host, port=port))
|
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.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||||
s.bind(addr)
|
s.bind(addr)
|
||||||
s.listen(5)
|
s.listen(5)
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
sock, addr = s.accept()
|
sock, addr = s.accept()
|
||||||
if not hasattr(sock, 'readline'): # pragma: no cover
|
create_thread(self.dispatch_request, sock, addr)
|
||||||
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()
|
|
||||||
|
|
||||||
def find_route(self, req):
|
def find_route(self, req):
|
||||||
f = None
|
f = None
|
||||||
@@ -329,7 +343,13 @@ class Microdot():
|
|||||||
break
|
break
|
||||||
return f
|
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)
|
f = self.find_route(req)
|
||||||
try:
|
try:
|
||||||
res = None
|
res = None
|
||||||
@@ -367,7 +387,14 @@ class Microdot():
|
|||||||
res = Response(*res)
|
res = Response(*res)
|
||||||
elif not isinstance(res, Response):
|
elif not isinstance(res, Response):
|
||||||
res = Response(res)
|
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
|
redirect = Response.redirect
|
||||||
|
|||||||
@@ -64,17 +64,11 @@ class Response(BaseResponse):
|
|||||||
|
|
||||||
class Microdot(BaseMicrodot):
|
class Microdot(BaseMicrodot):
|
||||||
def run(self, host='0.0.0.0', port=5000, debug=False):
|
def run(self, host='0.0.0.0', port=5000, debug=False):
|
||||||
async def serve(reader, writer):
|
self.debug = debug
|
||||||
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))
|
|
||||||
|
|
||||||
|
async def serve(reader, writer):
|
||||||
if not hasattr(writer, 'awrite'): # pragma: no cover
|
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):
|
async def awrite(self, data):
|
||||||
self.write(data)
|
self.write(data)
|
||||||
await self.drain()
|
await self.drain()
|
||||||
@@ -87,17 +81,18 @@ class Microdot(BaseMicrodot):
|
|||||||
writer.awrite = MethodType(awrite, writer)
|
writer.awrite = MethodType(awrite, writer)
|
||||||
writer.aclose = MethodType(aclose, writer)
|
writer.aclose = MethodType(aclose, writer)
|
||||||
|
|
||||||
await res.write(writer)
|
await self.dispatch_request(reader, writer)
|
||||||
await writer.aclose()
|
|
||||||
|
|
||||||
if debug: # pragma: no cover
|
if self.debug: # pragma: no cover
|
||||||
print('Listening on {host}:{port}...'.format(host=host, port=port))
|
print('Starting async server on {host}:{port}...'.format(
|
||||||
|
host=host, port=port))
|
||||||
loop = asyncio.get_event_loop()
|
loop = asyncio.get_event_loop()
|
||||||
loop.run_until_complete(asyncio.start_server(serve, host, port))
|
loop.run_until_complete(asyncio.start_server(serve, host, port))
|
||||||
loop.run_forever()
|
loop.run_forever()
|
||||||
loop.close() # pragma: no cover
|
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)
|
f = self.find_route(req)
|
||||||
try:
|
try:
|
||||||
res = None
|
res = None
|
||||||
@@ -137,7 +132,12 @@ class Microdot(BaseMicrodot):
|
|||||||
res = Response(*res)
|
res = Response(*res)
|
||||||
elif not isinstance(res, Response):
|
elif not isinstance(res, Response):
|
||||||
res = Response(res)
|
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):
|
async def _invoke_handler(self, f_or_coro, *args, **kwargs):
|
||||||
ret = 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
|
from tests import mock_socket
|
||||||
|
|
||||||
|
|
||||||
|
def mock_create_thread(f, *args, **kwargs):
|
||||||
|
f(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class TestMicrodot(unittest.TestCase):
|
class TestMicrodot(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
# mock socket module
|
# mock socket module
|
||||||
self.original_socket = sys.modules['microdot'].socket
|
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'].socket = mock_socket
|
||||||
|
sys.modules['microdot'].create_thread = mock_create_thread
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
# restore original socket module
|
# restore original socket module
|
||||||
sys.modules['microdot'].socket = self.original_socket
|
sys.modules['microdot'].socket = self.original_socket
|
||||||
|
sys.modules['microdot'].create_thread = self.original_create_thread
|
||||||
|
|
||||||
def test_get_request(self):
|
def test_get_request(self):
|
||||||
app = Microdot()
|
app = Microdot()
|
||||||
|
|||||||
Reference in New Issue
Block a user