Compare commits
20 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
89d942451f | ||
![]() |
4d6205e6fe | ||
![]() |
1684b0b986 | ||
![]() |
4f5faa4a3c | ||
![]() |
cbb77b536a | ||
![]() |
35c76253bf | ||
![]() |
8d86c3c598 | ||
![]() |
97635111af | ||
![]() |
7f3fe40cd4 | ||
![]() |
ea34bcd849 | ||
![]() |
05f758583b | ||
![]() |
760c74a293 | ||
![]() |
9def46beb8 | ||
![]() |
04be8e95a5 | ||
![]() |
78ced20fc7 | ||
![]() |
c3003413d3 | ||
![]() |
fe3fdc5d83 | ||
![]() |
b66fb6f9e8 | ||
![]() |
bf6175fb20 | ||
![]() |
58ca887be4 |
12
.github/FUNDING.yml
vendored
Normal file
12
.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# These are supported funding model platforms
|
||||||
|
|
||||||
|
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||||
|
patreon: # Replace with a single Patreon username
|
||||||
|
open_collective: sanic-org # Replace with a single Open Collective username
|
||||||
|
ko_fi: # Replace with a single Ko-fi username
|
||||||
|
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||||
|
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||||
|
liberapay: # Replace with a single Liberapay username
|
||||||
|
issuehunt: # Replace with a single IssueHunt username
|
||||||
|
otechie: # Replace with a single Otechie username
|
||||||
|
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
@ -1,3 +1,90 @@
|
|||||||
|
Version 20.12.0
|
||||||
|
===============
|
||||||
|
|
||||||
|
Features
|
||||||
|
********
|
||||||
|
|
||||||
|
*
|
||||||
|
`#1945 <https://github.com/huge-success/sanic/pull/1945>`_
|
||||||
|
Static route more verbose if file not found
|
||||||
|
|
||||||
|
*
|
||||||
|
`#1954 <https://github.com/huge-success/sanic/pull/1954>`_
|
||||||
|
Fix static routes registration on a blueprint
|
||||||
|
|
||||||
|
*
|
||||||
|
`#1961 <https://github.com/huge-success/sanic/pull/1961>`_
|
||||||
|
Add Python 3.9 support
|
||||||
|
|
||||||
|
*
|
||||||
|
`#1962 <https://github.com/huge-success/sanic/pull/1962>`_
|
||||||
|
Sanic CLI upgrade
|
||||||
|
|
||||||
|
*
|
||||||
|
`#1967 <https://github.com/huge-success/sanic/pull/1967>`_
|
||||||
|
Update aiofile version requirements
|
||||||
|
|
||||||
|
*
|
||||||
|
`#1969 <https://github.com/huge-success/sanic/pull/1969>`_
|
||||||
|
Update multidict version requirements
|
||||||
|
|
||||||
|
*
|
||||||
|
`#1970 <https://github.com/huge-success/sanic/pull/1970>`_
|
||||||
|
Add py.typed file
|
||||||
|
|
||||||
|
*
|
||||||
|
`#1972 <https://github.com/huge-success/sanic/pull/1972>`_
|
||||||
|
Speed optimization in request handler
|
||||||
|
|
||||||
|
*
|
||||||
|
`#1979 <https://github.com/huge-success/sanic/pull/1979>`_
|
||||||
|
Add app registry and Sanic class level app retrieval
|
||||||
|
|
||||||
|
Bugfixes
|
||||||
|
********
|
||||||
|
|
||||||
|
*
|
||||||
|
`#1965 <https://github.com/huge-success/sanic/pull/1965>`_
|
||||||
|
Fix Chunked Transport-Encoding in ASGI streaming response
|
||||||
|
|
||||||
|
Deprecations and Removals
|
||||||
|
*************************
|
||||||
|
|
||||||
|
*
|
||||||
|
`#1981 <https://github.com/huge-success/sanic/pull/1981>`_
|
||||||
|
Cleanup and remove deprecated code
|
||||||
|
|
||||||
|
Developer infrastructure
|
||||||
|
************************
|
||||||
|
|
||||||
|
*
|
||||||
|
`#1956 <https://github.com/huge-success/sanic/pull/1956>`_
|
||||||
|
Fix load module test
|
||||||
|
|
||||||
|
*
|
||||||
|
`#1973 <https://github.com/huge-success/sanic/pull/1973>`_
|
||||||
|
Transition Travis from .org to .com
|
||||||
|
|
||||||
|
*
|
||||||
|
`#1986 <https://github.com/huge-success/sanic/pull/1986>`_
|
||||||
|
Update tox requirements
|
||||||
|
|
||||||
|
Improved Documentation
|
||||||
|
**********************
|
||||||
|
|
||||||
|
*
|
||||||
|
`#1951 <https://github.com/huge-success/sanic/pull/1951>`_
|
||||||
|
Documentation improvements
|
||||||
|
|
||||||
|
*
|
||||||
|
`#1983 <https://github.com/huge-success/sanic/pull/1983>`_
|
||||||
|
Remove duplicate contents in testing.rst
|
||||||
|
|
||||||
|
*
|
||||||
|
`#1984 <https://github.com/huge-success/sanic/pull/1984>`_
|
||||||
|
Fix typo in routing.rst
|
||||||
|
|
||||||
|
|
||||||
Version 20.9.1
|
Version 20.9.1
|
||||||
===============
|
===============
|
||||||
|
|
||||||
|
@ -1 +1 @@
|
|||||||
__version__ = "20.9.1"
|
__version__ = "20.12.4"
|
||||||
|
28
sanic/app.py
28
sanic/app.py
@ -12,6 +12,7 @@ from ssl import Purpose, SSLContext, create_default_context
|
|||||||
from traceback import format_exc
|
from traceback import format_exc
|
||||||
from typing import Any, Dict, Optional, Type, Union
|
from typing import Any, Dict, Optional, Type, Union
|
||||||
from urllib.parse import urlencode, urlunparse
|
from urllib.parse import urlencode, urlunparse
|
||||||
|
from warnings import warn
|
||||||
|
|
||||||
from sanic import reloader_helpers
|
from sanic import reloader_helpers
|
||||||
from sanic.asgi import ASGIApp
|
from sanic.asgi import ASGIApp
|
||||||
@ -50,6 +51,7 @@ class Sanic:
|
|||||||
strict_slashes=False,
|
strict_slashes=False,
|
||||||
log_config=None,
|
log_config=None,
|
||||||
configure_logging=True,
|
configure_logging=True,
|
||||||
|
register=None,
|
||||||
):
|
):
|
||||||
|
|
||||||
# Get name from previous stack frame
|
# Get name from previous stack frame
|
||||||
@ -88,6 +90,10 @@ class Sanic:
|
|||||||
# Register alternative method names
|
# Register alternative method names
|
||||||
self.go_fast = self.run
|
self.go_fast = self.run
|
||||||
|
|
||||||
|
if register is not None:
|
||||||
|
self.config.REGISTER = register
|
||||||
|
|
||||||
|
if self.config.REGISTER:
|
||||||
self.__class__.register_app(self)
|
self.__class__.register_app(self)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -489,9 +495,7 @@ class Sanic:
|
|||||||
websocket_handler = partial(
|
websocket_handler = partial(
|
||||||
self._websocket_handler, handler, subprotocols=subprotocols
|
self._websocket_handler, handler, subprotocols=subprotocols
|
||||||
)
|
)
|
||||||
websocket_handler.__name__ = (
|
websocket_handler.__name__ = handler.__name__
|
||||||
"websocket_handler_" + handler.__name__
|
|
||||||
)
|
|
||||||
routes.extend(
|
routes.extend(
|
||||||
self.router.add(
|
self.router.add(
|
||||||
uri=uri,
|
uri=uri,
|
||||||
@ -742,6 +746,24 @@ class Sanic:
|
|||||||
kw.update(name=view_name)
|
kw.update(name=view_name)
|
||||||
|
|
||||||
uri, route = self.router.find_route_by_view_name(view_name, **kw)
|
uri, route = self.router.find_route_by_view_name(view_name, **kw)
|
||||||
|
|
||||||
|
# TODO(laggardkernel): this fix should be removed in v21.3.
|
||||||
|
# Try again without the unnecessary prefix "websocket_handler_",
|
||||||
|
# which was added by accident on non-blueprint handlers. GH-2021
|
||||||
|
if not (uri and route) and view_name.startswith("websocket_handler_"):
|
||||||
|
view_name = view_name[18:]
|
||||||
|
uri, route = self.router.find_route_by_view_name(view_name, **kw)
|
||||||
|
if uri and route:
|
||||||
|
warn(
|
||||||
|
"The bug of adding unnecessary `websocket_handler_` "
|
||||||
|
"prefix in param `view_name` for non-blueprint handlers "
|
||||||
|
"is fixed. This backward support will be removed in "
|
||||||
|
"v21.3. Please update `Sanic.url_for()` callings in your "
|
||||||
|
"code soon.",
|
||||||
|
DeprecationWarning,
|
||||||
|
stacklevel=2,
|
||||||
|
)
|
||||||
|
|
||||||
if not (uri and route):
|
if not (uri and route):
|
||||||
raise URLBuildError(
|
raise URLBuildError(
|
||||||
f"Endpoint with name `{view_name}` was not found"
|
f"Endpoint with name `{view_name}` was not found"
|
||||||
|
@ -40,6 +40,7 @@ DEFAULT_CONFIG = {
|
|||||||
"PROXIES_COUNT": None,
|
"PROXIES_COUNT": None,
|
||||||
"FORWARDED_FOR_HEADER": "X-Forwarded-For",
|
"FORWARDED_FOR_HEADER": "X-Forwarded-For",
|
||||||
"FALLBACK_ERROR_FORMAT": "html",
|
"FALLBACK_ERROR_FORMAT": "html",
|
||||||
|
"REGISTER": True,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -265,9 +265,12 @@ class Request:
|
|||||||
:type errors: str
|
:type errors: str
|
||||||
:return: RequestParameters
|
:return: RequestParameters
|
||||||
"""
|
"""
|
||||||
if not self.parsed_args[
|
if (
|
||||||
(keep_blank_values, strict_parsing, encoding, errors)
|
keep_blank_values,
|
||||||
]:
|
strict_parsing,
|
||||||
|
encoding,
|
||||||
|
errors,
|
||||||
|
) not in self.parsed_args:
|
||||||
if self.query_string:
|
if self.query_string:
|
||||||
self.parsed_args[
|
self.parsed_args[
|
||||||
(keep_blank_values, strict_parsing, encoding, errors)
|
(keep_blank_values, strict_parsing, encoding, errors)
|
||||||
@ -321,9 +324,12 @@ class Request:
|
|||||||
:type errors: str
|
:type errors: str
|
||||||
:return: list
|
:return: list
|
||||||
"""
|
"""
|
||||||
if not self.parsed_not_grouped_args[
|
if (
|
||||||
(keep_blank_values, strict_parsing, encoding, errors)
|
keep_blank_values,
|
||||||
]:
|
strict_parsing,
|
||||||
|
encoding,
|
||||||
|
errors,
|
||||||
|
) not in self.parsed_not_grouped_args:
|
||||||
if self.query_string:
|
if self.query_string:
|
||||||
self.parsed_not_grouped_args[
|
self.parsed_not_grouped_args[
|
||||||
(keep_blank_values, strict_parsing, encoding, errors)
|
(keep_blank_values, strict_parsing, encoding, errors)
|
||||||
|
3
setup.py
3
setup.py
@ -57,7 +57,8 @@ setup_kwargs = {
|
|||||||
"author": "Sanic Community",
|
"author": "Sanic Community",
|
||||||
"author_email": "admhpkns@gmail.com",
|
"author_email": "admhpkns@gmail.com",
|
||||||
"description": (
|
"description": (
|
||||||
"A web server and web framework that's written to go fast. Build fast. Run fast."
|
"A web server and web framework that's written to go fast. "
|
||||||
|
"Build fast. Run fast."
|
||||||
),
|
),
|
||||||
"long_description": long_description,
|
"long_description": long_description,
|
||||||
"packages": ["sanic"],
|
"packages": ["sanic"],
|
||||||
|
@ -3,6 +3,7 @@ import logging
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
from inspect import isawaitable
|
from inspect import isawaitable
|
||||||
|
from os import environ
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
@ -290,6 +291,7 @@ def test_app_registry_name_reuse():
|
|||||||
with pytest.raises(SanicException):
|
with pytest.raises(SanicException):
|
||||||
Sanic("test")
|
Sanic("test")
|
||||||
Sanic.test_mode = True
|
Sanic.test_mode = True
|
||||||
|
Sanic("test")
|
||||||
|
|
||||||
|
|
||||||
def test_app_registry_retrieval():
|
def test_app_registry_retrieval():
|
||||||
@ -306,3 +308,17 @@ def test_get_app_does_not_exist_force_create():
|
|||||||
assert isinstance(
|
assert isinstance(
|
||||||
Sanic.get_app("does-not-exist", force_create=True), Sanic
|
Sanic.get_app("does-not-exist", force_create=True), Sanic
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_app_no_registry():
|
||||||
|
Sanic("no-register", register=False)
|
||||||
|
with pytest.raises(SanicException):
|
||||||
|
Sanic.get_app("no-register")
|
||||||
|
|
||||||
|
|
||||||
|
def test_app_no_registry_env():
|
||||||
|
environ["SANIC_REGISTER"] = "False"
|
||||||
|
Sanic("no-register")
|
||||||
|
with pytest.raises(SanicException):
|
||||||
|
Sanic.get_app("no-register")
|
||||||
|
del environ["SANIC_REGISTER"]
|
||||||
|
@ -289,6 +289,17 @@ def test_query_string(app):
|
|||||||
assert request.args.getlist("test1") == ["1"]
|
assert request.args.getlist("test1") == ["1"]
|
||||||
assert request.args.get("test3", default="My value") == "My value"
|
assert request.args.get("test3", default="My value") == "My value"
|
||||||
|
|
||||||
|
def test_popped_stays_popped(app):
|
||||||
|
@app.route("/")
|
||||||
|
async def handler(request):
|
||||||
|
return text("OK")
|
||||||
|
|
||||||
|
request, response = app.test_client.get(
|
||||||
|
"/", params=[("test1", "1")]
|
||||||
|
)
|
||||||
|
|
||||||
|
assert request.args.pop("test1") == ["1"]
|
||||||
|
assert "test1" not in request.args
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_query_string_asgi(app):
|
async def test_query_string_asgi(app):
|
||||||
|
@ -348,3 +348,13 @@ def test_methodview_naming(methodview_app):
|
|||||||
|
|
||||||
assert viewone_url == "/view_one"
|
assert viewone_url == "/view_one"
|
||||||
assert viewtwo_url == "/view_two"
|
assert viewtwo_url == "/view_two"
|
||||||
|
|
||||||
|
|
||||||
|
def test_url_for_with_websocket_handlers(app):
|
||||||
|
# Test for a specific bugfix in GH-2021
|
||||||
|
@app.websocket("/ws")
|
||||||
|
async def my_handler(request, ws):
|
||||||
|
pass
|
||||||
|
|
||||||
|
assert app.url_for("my_handler") == "/ws"
|
||||||
|
assert app.url_for("websocket_handler_my_handler") == "/ws"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user