Merge pull request #1581 from huge-success/fix-build-time
Fix build time
This commit is contained in:
commit
21ebf6d777
|
@ -13,7 +13,7 @@ dependencies:
|
||||||
- sphinx==1.8.3
|
- sphinx==1.8.3
|
||||||
- sphinx_rtd_theme==0.4.2
|
- sphinx_rtd_theme==0.4.2
|
||||||
- recommonmark==0.5.0
|
- recommonmark==0.5.0
|
||||||
- requests-async==0.4.0
|
- requests-async==0.5.0
|
||||||
- sphinxcontrib-asyncio>=0.2.0
|
- sphinxcontrib-asyncio>=0.2.0
|
||||||
- docutils==0.14
|
- docutils==0.14
|
||||||
- pygments==2.3.1
|
- pygments==2.3.1
|
||||||
|
|
4
setup.py
4
setup.py
|
@ -89,8 +89,8 @@ tests_require = [
|
||||||
"multidict>=4.0,<5.0",
|
"multidict>=4.0,<5.0",
|
||||||
"gunicorn",
|
"gunicorn",
|
||||||
"pytest-cov",
|
"pytest-cov",
|
||||||
"httpcore==0.1.1",
|
"httpcore==0.3.0",
|
||||||
"requests-async==0.4.0",
|
"requests-async==0.5.0",
|
||||||
"beautifulsoup4",
|
"beautifulsoup4",
|
||||||
uvloop,
|
uvloop,
|
||||||
ujson,
|
ujson,
|
||||||
|
|
|
@ -16,45 +16,28 @@ from sanic.response import text
|
||||||
from sanic.testing import HOST, PORT, SanicTestClient
|
from sanic.testing import HOST, PORT, SanicTestClient
|
||||||
|
|
||||||
|
|
||||||
# import traceback
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
CONFIG_FOR_TESTS = {"KEEP_ALIVE_TIMEOUT": 2, "KEEP_ALIVE": True}
|
CONFIG_FOR_TESTS = {"KEEP_ALIVE_TIMEOUT": 2, "KEEP_ALIVE": True}
|
||||||
|
|
||||||
old_conn = None
|
old_conn = None
|
||||||
|
|
||||||
|
|
||||||
class ReusableSanicConnectionPool(httpcore.ConnectionPool):
|
class ReusableSanicConnectionPool(httpcore.ConnectionPool):
|
||||||
async def acquire_connection(self, url, ssl, timeout):
|
async def acquire_connection(self, origin):
|
||||||
global old_conn
|
global old_conn
|
||||||
if timeout.connect_timeout and not timeout.pool_timeout:
|
connection = self.active_connections.pop_by_origin(origin, http2_only=True)
|
||||||
timeout.pool_timeout = timeout.connect_timeout
|
if connection is None:
|
||||||
key = (url.scheme, url.hostname, url.port, ssl, timeout)
|
connection = self.keepalive_connections.pop_by_origin(origin)
|
||||||
try:
|
|
||||||
connection = self._keepalive_connections[key].pop()
|
|
||||||
if not self._keepalive_connections[key]:
|
|
||||||
del self._keepalive_connections[key]
|
|
||||||
self.num_keepalive_connections -= 1
|
|
||||||
self.num_active_connections += 1
|
|
||||||
|
|
||||||
except (KeyError, IndexError):
|
if connection is None:
|
||||||
ssl_context = await self.get_ssl_context(url, ssl)
|
await self.max_connections.acquire()
|
||||||
try:
|
connection = httpcore.HTTPConnection(
|
||||||
await asyncio.wait_for(
|
origin,
|
||||||
self._max_connections.acquire(), timeout.pool_timeout
|
ssl=self.ssl,
|
||||||
|
timeout=self.timeout,
|
||||||
|
backend=self.backend,
|
||||||
|
release_func=self.release_connection,
|
||||||
)
|
)
|
||||||
except asyncio.TimeoutError:
|
self.active_connections.add(connection)
|
||||||
raise PoolTimeout()
|
|
||||||
release = functools.partial(self.release_connection, key=key)
|
|
||||||
connection = httpcore.connections.Connection(
|
|
||||||
timeout=timeout, on_release=release
|
|
||||||
)
|
|
||||||
self.num_active_connections += 1
|
|
||||||
await connection.open(url.hostname, url.port, ssl=ssl_context)
|
|
||||||
|
|
||||||
if old_conn is not None:
|
if old_conn is not None:
|
||||||
if old_conn != connection:
|
if old_conn != connection:
|
||||||
|
@ -70,62 +53,6 @@ class ReusableSanicAdapter(requests.adapters.HTTPAdapter):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.pool = ReusableSanicConnectionPool()
|
self.pool = ReusableSanicConnectionPool()
|
||||||
|
|
||||||
async def send(
|
|
||||||
self,
|
|
||||||
request,
|
|
||||||
stream=False,
|
|
||||||
timeout=None,
|
|
||||||
verify=True,
|
|
||||||
cert=None,
|
|
||||||
proxies=None,
|
|
||||||
):
|
|
||||||
|
|
||||||
method = request.method
|
|
||||||
url = request.url
|
|
||||||
headers = [
|
|
||||||
(_encode(k), _encode(v)) for k, v in request.headers.items()
|
|
||||||
]
|
|
||||||
|
|
||||||
if not request.body:
|
|
||||||
body = b""
|
|
||||||
elif isinstance(request.body, str):
|
|
||||||
body = _encode(request.body)
|
|
||||||
else:
|
|
||||||
body = request.body
|
|
||||||
|
|
||||||
if isinstance(timeout, tuple):
|
|
||||||
timeout_kwargs = {
|
|
||||||
"connect_timeout": timeout[0],
|
|
||||||
"read_timeout": timeout[1],
|
|
||||||
}
|
|
||||||
else:
|
|
||||||
timeout_kwargs = {
|
|
||||||
"connect_timeout": timeout,
|
|
||||||
"read_timeout": timeout,
|
|
||||||
}
|
|
||||||
|
|
||||||
ssl = httpcore.SSLConfig(cert=cert, verify=verify)
|
|
||||||
timeout = httpcore.TimeoutConfig(**timeout_kwargs)
|
|
||||||
|
|
||||||
try:
|
|
||||||
response = await self.pool.request(
|
|
||||||
method,
|
|
||||||
url,
|
|
||||||
headers=headers,
|
|
||||||
body=body,
|
|
||||||
stream=stream,
|
|
||||||
ssl=ssl,
|
|
||||||
timeout=timeout,
|
|
||||||
)
|
|
||||||
except (httpcore.BadResponse, socket.error) as err:
|
|
||||||
raise ConnectionError(err, request=request)
|
|
||||||
except httpcore.ConnectTimeout as err:
|
|
||||||
raise requests.exceptions.ConnectTimeout(err, request=request)
|
|
||||||
except httpcore.ReadTimeout as err:
|
|
||||||
raise requests.exceptions.ReadTimeout(err, request=request)
|
|
||||||
|
|
||||||
return self.build_response(request, response)
|
|
||||||
|
|
||||||
|
|
||||||
class ResusableSanicSession(requests.Session):
|
class ResusableSanicSession(requests.Session):
|
||||||
def __init__(self, *args, **kwargs) -> None:
|
def __init__(self, *args, **kwargs) -> None:
|
||||||
|
@ -153,13 +80,14 @@ class ReuseableSanicTestClient(SanicTestClient):
|
||||||
uri="/",
|
uri="/",
|
||||||
gather_request=True,
|
gather_request=True,
|
||||||
debug=False,
|
debug=False,
|
||||||
server_kwargs={"return_asyncio_server": True},
|
server_kwargs=None,
|
||||||
*request_args,
|
*request_args,
|
||||||
**request_kwargs,
|
**request_kwargs,
|
||||||
):
|
):
|
||||||
loop = self._loop
|
loop = self._loop
|
||||||
results = [None, None]
|
results = [None, None]
|
||||||
exceptions = []
|
exceptions = []
|
||||||
|
server_kwargs = server_kwargs or {"return_asyncio_server": True}
|
||||||
if gather_request:
|
if gather_request:
|
||||||
|
|
||||||
def _collect_request(request):
|
def _collect_request(request):
|
||||||
|
@ -187,7 +115,6 @@ class ReuseableSanicTestClient(SanicTestClient):
|
||||||
)
|
)
|
||||||
results[-1] = response
|
results[-1] = response
|
||||||
except Exception as e2:
|
except Exception as e2:
|
||||||
# traceback.print_tb(e2.__traceback__)
|
|
||||||
exceptions.append(e2)
|
exceptions.append(e2)
|
||||||
|
|
||||||
if self._server is not None:
|
if self._server is not None:
|
||||||
|
@ -205,7 +132,6 @@ class ReuseableSanicTestClient(SanicTestClient):
|
||||||
loop._stopping = False
|
loop._stopping = False
|
||||||
_server = loop.run_until_complete(_server_co)
|
_server = loop.run_until_complete(_server_co)
|
||||||
except Exception as e1:
|
except Exception as e1:
|
||||||
# traceback.print_tb(e1.__traceback__)
|
|
||||||
raise e1
|
raise e1
|
||||||
self._server = _server
|
self._server = _server
|
||||||
server.trigger_events(self.app.listeners["after_server_start"], loop)
|
server.trigger_events(self.app.listeners["after_server_start"], loop)
|
||||||
|
@ -257,14 +183,10 @@ class ReuseableSanicTestClient(SanicTestClient):
|
||||||
request_keepalive = kwargs.pop(
|
request_keepalive = kwargs.pop(
|
||||||
"request_keepalive", CONFIG_FOR_TESTS["KEEP_ALIVE_TIMEOUT"]
|
"request_keepalive", CONFIG_FOR_TESTS["KEEP_ALIVE_TIMEOUT"]
|
||||||
)
|
)
|
||||||
if self._session:
|
if not self._session:
|
||||||
_session = self._session
|
self._session = ResusableSanicSession()
|
||||||
else:
|
|
||||||
_session = ResusableSanicSession()
|
|
||||||
self._session = _session
|
|
||||||
async with _session as session:
|
|
||||||
try:
|
try:
|
||||||
response = await getattr(session, method.lower())(
|
response = await getattr(self._session, method.lower())(
|
||||||
url,
|
url,
|
||||||
verify=False,
|
verify=False,
|
||||||
timeout=request_keepalive,
|
timeout=request_keepalive,
|
||||||
|
@ -319,6 +241,7 @@ def test_keep_alive_timeout_reuse():
|
||||||
"""If the server keep-alive timeout and client keep-alive timeout are
|
"""If the server keep-alive timeout and client keep-alive timeout are
|
||||||
both longer than the delay, the client _and_ server will successfully
|
both longer than the delay, the client _and_ server will successfully
|
||||||
reuse the existing connection."""
|
reuse the existing connection."""
|
||||||
|
try:
|
||||||
loop = asyncio.new_event_loop()
|
loop = asyncio.new_event_loop()
|
||||||
asyncio.set_event_loop(loop)
|
asyncio.set_event_loop(loop)
|
||||||
client = ReuseableSanicTestClient(keep_alive_timeout_app_reuse, loop)
|
client = ReuseableSanicTestClient(keep_alive_timeout_app_reuse, loop)
|
||||||
|
@ -330,12 +253,14 @@ def test_keep_alive_timeout_reuse():
|
||||||
request, response = client.get("/1")
|
request, response = client.get("/1")
|
||||||
assert response.status == 200
|
assert response.status == 200
|
||||||
assert response.text == "OK"
|
assert response.text == "OK"
|
||||||
|
finally:
|
||||||
client.kill_server()
|
client.kill_server()
|
||||||
|
|
||||||
|
|
||||||
def test_keep_alive_client_timeout():
|
def test_keep_alive_client_timeout():
|
||||||
"""If the server keep-alive timeout is longer than the client
|
"""If the server keep-alive timeout is longer than the client
|
||||||
keep-alive timeout, client will try to create a new connection here."""
|
keep-alive timeout, client will try to create a new connection here."""
|
||||||
|
try:
|
||||||
loop = asyncio.new_event_loop()
|
loop = asyncio.new_event_loop()
|
||||||
asyncio.set_event_loop(loop)
|
asyncio.set_event_loop(loop)
|
||||||
client = ReuseableSanicTestClient(keep_alive_app_client_timeout, loop)
|
client = ReuseableSanicTestClient(keep_alive_app_client_timeout, loop)
|
||||||
|
@ -354,6 +279,7 @@ def test_keep_alive_client_timeout():
|
||||||
assert exception is not None
|
assert exception is not None
|
||||||
assert isinstance(exception, ValueError)
|
assert isinstance(exception, ValueError)
|
||||||
assert "got a new connection" in exception.args[0]
|
assert "got a new connection" in exception.args[0]
|
||||||
|
finally:
|
||||||
client.kill_server()
|
client.kill_server()
|
||||||
|
|
||||||
|
|
||||||
|
@ -362,6 +288,7 @@ def test_keep_alive_server_timeout():
|
||||||
keep-alive timeout, the client will either a 'Connection reset' error
|
keep-alive timeout, the client will either a 'Connection reset' error
|
||||||
_or_ a new connection. Depending on how the event-loop handles the
|
_or_ a new connection. Depending on how the event-loop handles the
|
||||||
broken server connection."""
|
broken server connection."""
|
||||||
|
try:
|
||||||
loop = asyncio.new_event_loop()
|
loop = asyncio.new_event_loop()
|
||||||
asyncio.set_event_loop(loop)
|
asyncio.set_event_loop(loop)
|
||||||
client = ReuseableSanicTestClient(keep_alive_app_server_timeout, loop)
|
client = ReuseableSanicTestClient(keep_alive_app_server_timeout, loop)
|
||||||
|
@ -383,4 +310,5 @@ def test_keep_alive_server_timeout():
|
||||||
"Connection reset" in exception.args[0]
|
"Connection reset" in exception.args[0]
|
||||||
or "got a new connection" in exception.args[0]
|
or "got a new connection" in exception.args[0]
|
||||||
)
|
)
|
||||||
|
finally:
|
||||||
client.kill_server()
|
client.kill_server()
|
||||||
|
|
|
@ -71,15 +71,13 @@ def test_request_stream_app(app):
|
||||||
@app.post("/post/<id>", stream=True)
|
@app.post("/post/<id>", stream=True)
|
||||||
async def post(request, id):
|
async def post(request, id):
|
||||||
assert isinstance(request.stream, StreamBuffer)
|
assert isinstance(request.stream, StreamBuffer)
|
||||||
|
result = ""
|
||||||
async def streaming(response):
|
|
||||||
while True:
|
while True:
|
||||||
body = await request.stream.read()
|
body = await request.stream.read()
|
||||||
if body is None:
|
if body is None:
|
||||||
break
|
break
|
||||||
await response.write(body.decode("utf-8"))
|
result += body.decode("utf-8")
|
||||||
|
return text(result)
|
||||||
return stream(streaming)
|
|
||||||
|
|
||||||
@app.put("/_put")
|
@app.put("/_put")
|
||||||
async def _put(request):
|
async def _put(request):
|
||||||
|
@ -89,15 +87,13 @@ def test_request_stream_app(app):
|
||||||
@app.put("/put", stream=True)
|
@app.put("/put", stream=True)
|
||||||
async def put(request):
|
async def put(request):
|
||||||
assert isinstance(request.stream, StreamBuffer)
|
assert isinstance(request.stream, StreamBuffer)
|
||||||
|
result = ""
|
||||||
async def streaming(response):
|
|
||||||
while True:
|
while True:
|
||||||
body = await request.stream.read()
|
body = await request.stream.read()
|
||||||
if body is None:
|
if body is None:
|
||||||
break
|
break
|
||||||
await response.write(body.decode("utf-8"))
|
result += body.decode("utf-8")
|
||||||
|
return text(result)
|
||||||
return stream(streaming)
|
|
||||||
|
|
||||||
@app.patch("/_patch")
|
@app.patch("/_patch")
|
||||||
async def _patch(request):
|
async def _patch(request):
|
||||||
|
@ -107,15 +103,14 @@ def test_request_stream_app(app):
|
||||||
@app.patch("/patch", stream=True)
|
@app.patch("/patch", stream=True)
|
||||||
async def patch(request):
|
async def patch(request):
|
||||||
assert isinstance(request.stream, StreamBuffer)
|
assert isinstance(request.stream, StreamBuffer)
|
||||||
|
result = ""
|
||||||
async def streaming(response):
|
|
||||||
while True:
|
while True:
|
||||||
body = await request.stream.read()
|
body = await request.stream.read()
|
||||||
if body is None:
|
if body is None:
|
||||||
break
|
break
|
||||||
await response.write(body.decode("utf-8"))
|
result += body.decode("utf-8")
|
||||||
|
return text(result)
|
||||||
|
|
||||||
return stream(streaming)
|
|
||||||
|
|
||||||
assert app.is_request_stream is True
|
assert app.is_request_stream is True
|
||||||
|
|
||||||
|
@ -166,15 +161,13 @@ def test_request_stream_handle_exception(app):
|
||||||
@app.post("/post/<id>", stream=True)
|
@app.post("/post/<id>", stream=True)
|
||||||
async def post(request, id):
|
async def post(request, id):
|
||||||
assert isinstance(request.stream, StreamBuffer)
|
assert isinstance(request.stream, StreamBuffer)
|
||||||
|
result = ""
|
||||||
async def streaming(response):
|
|
||||||
while True:
|
while True:
|
||||||
body = await request.stream.read()
|
body = await request.stream.read()
|
||||||
if body is None:
|
if body is None:
|
||||||
break
|
break
|
||||||
await response.write(body.decode("utf-8"))
|
result += body.decode("utf-8")
|
||||||
|
return text(result)
|
||||||
return stream(streaming)
|
|
||||||
|
|
||||||
# 404
|
# 404
|
||||||
request, response = app.test_client.post("/in_valid_post", data=data)
|
request, response = app.test_client.post("/in_valid_post", data=data)
|
||||||
|
@ -222,15 +215,13 @@ def test_request_stream_blueprint(app):
|
||||||
@bp.post("/post/<id>", stream=True)
|
@bp.post("/post/<id>", stream=True)
|
||||||
async def post(request, id):
|
async def post(request, id):
|
||||||
assert isinstance(request.stream, StreamBuffer)
|
assert isinstance(request.stream, StreamBuffer)
|
||||||
|
result = ""
|
||||||
async def streaming(response):
|
|
||||||
while True:
|
while True:
|
||||||
body = await request.stream.read()
|
body = await request.stream.read()
|
||||||
if body is None:
|
if body is None:
|
||||||
break
|
break
|
||||||
await response.write(body.decode("utf-8"))
|
result += body.decode("utf-8")
|
||||||
|
return text(result)
|
||||||
return stream(streaming)
|
|
||||||
|
|
||||||
@bp.put("/_put")
|
@bp.put("/_put")
|
||||||
async def _put(request):
|
async def _put(request):
|
||||||
|
@ -240,15 +231,13 @@ def test_request_stream_blueprint(app):
|
||||||
@bp.put("/put", stream=True)
|
@bp.put("/put", stream=True)
|
||||||
async def put(request):
|
async def put(request):
|
||||||
assert isinstance(request.stream, StreamBuffer)
|
assert isinstance(request.stream, StreamBuffer)
|
||||||
|
result = ""
|
||||||
async def streaming(response):
|
|
||||||
while True:
|
while True:
|
||||||
body = await request.stream.read()
|
body = await request.stream.read()
|
||||||
if body is None:
|
if body is None:
|
||||||
break
|
break
|
||||||
await response.write(body.decode("utf-8"))
|
result += body.decode("utf-8")
|
||||||
|
return text(result)
|
||||||
return stream(streaming)
|
|
||||||
|
|
||||||
@bp.patch("/_patch")
|
@bp.patch("/_patch")
|
||||||
async def _patch(request):
|
async def _patch(request):
|
||||||
|
@ -258,27 +247,23 @@ def test_request_stream_blueprint(app):
|
||||||
@bp.patch("/patch", stream=True)
|
@bp.patch("/patch", stream=True)
|
||||||
async def patch(request):
|
async def patch(request):
|
||||||
assert isinstance(request.stream, StreamBuffer)
|
assert isinstance(request.stream, StreamBuffer)
|
||||||
|
result = ""
|
||||||
async def streaming(response):
|
|
||||||
while True:
|
while True:
|
||||||
body = await request.stream.read()
|
body = await request.stream.read()
|
||||||
if body is None:
|
if body is None:
|
||||||
break
|
break
|
||||||
await response.write(body.decode("utf-8"))
|
result += body.decode("utf-8")
|
||||||
|
return text(result)
|
||||||
return stream(streaming)
|
|
||||||
|
|
||||||
async def post_add_route(request):
|
async def post_add_route(request):
|
||||||
assert isinstance(request.stream, StreamBuffer)
|
assert isinstance(request.stream, StreamBuffer)
|
||||||
|
result = ""
|
||||||
async def streaming(response):
|
|
||||||
while True:
|
while True:
|
||||||
body = await request.stream.read()
|
body = await request.stream.read()
|
||||||
if body is None:
|
if body is None:
|
||||||
break
|
break
|
||||||
await response.write(body.decode("utf-8"))
|
result += body.decode("utf-8")
|
||||||
|
return text(result)
|
||||||
return stream(streaming)
|
|
||||||
|
|
||||||
bp.add_route(
|
bp.add_route(
|
||||||
post_add_route, "/post/add_route", methods=["POST"], stream=True
|
post_add_route, "/post/add_route", methods=["POST"], stream=True
|
||||||
|
@ -388,15 +373,13 @@ def test_request_stream(app):
|
||||||
@app.post("/stream", stream=True)
|
@app.post("/stream", stream=True)
|
||||||
async def handler(request):
|
async def handler(request):
|
||||||
assert isinstance(request.stream, StreamBuffer)
|
assert isinstance(request.stream, StreamBuffer)
|
||||||
|
result = ""
|
||||||
async def streaming(response):
|
|
||||||
while True:
|
while True:
|
||||||
body = await request.stream.read()
|
body = await request.stream.read()
|
||||||
if body is None:
|
if body is None:
|
||||||
break
|
break
|
||||||
await response.write(body.decode("utf-8"))
|
result += body.decode("utf-8")
|
||||||
|
return text(result)
|
||||||
return stream(streaming)
|
|
||||||
|
|
||||||
@app.get("/get")
|
@app.get("/get")
|
||||||
async def get(request):
|
async def get(request):
|
||||||
|
|
|
@ -13,37 +13,26 @@ class DelayableSanicConnectionPool(httpcore.ConnectionPool):
|
||||||
self._request_delay = request_delay
|
self._request_delay = request_delay
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
async def request(
|
async def send(
|
||||||
self,
|
self,
|
||||||
method,
|
request,
|
||||||
url,
|
|
||||||
headers=(),
|
|
||||||
body=b"",
|
|
||||||
stream=False,
|
stream=False,
|
||||||
ssl=None,
|
ssl=None,
|
||||||
timeout=None,
|
timeout=None,
|
||||||
):
|
):
|
||||||
if ssl is None:
|
connection = await self.acquire_connection(request.url.origin)
|
||||||
ssl = self.ssl_config
|
if connection.h11_connection is None and connection.h2_connection is None:
|
||||||
if timeout is None:
|
await connection.connect(ssl=ssl, timeout=timeout)
|
||||||
timeout = self.timeout
|
|
||||||
|
|
||||||
parsed_url = httpcore.URL(url)
|
|
||||||
request = httpcore.Request(
|
|
||||||
method, parsed_url, headers=headers, body=body
|
|
||||||
)
|
|
||||||
connection = await self.acquire_connection(
|
|
||||||
parsed_url, ssl=ssl, timeout=timeout
|
|
||||||
)
|
|
||||||
if self._request_delay:
|
if self._request_delay:
|
||||||
print(f"\t>> Sleeping ({self._request_delay})")
|
|
||||||
await asyncio.sleep(self._request_delay)
|
await asyncio.sleep(self._request_delay)
|
||||||
response = await connection.send(request)
|
|
||||||
if not stream:
|
|
||||||
try:
|
try:
|
||||||
await response.read()
|
response = await connection.send(
|
||||||
finally:
|
request, stream=stream, ssl=ssl, timeout=timeout
|
||||||
await response.close()
|
)
|
||||||
|
except BaseException as exc:
|
||||||
|
self.active_connections.remove(connection)
|
||||||
|
self.max_connections.release()
|
||||||
|
raise exc
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user