diff --git a/.appveyor.yml b/.appveyor.yml index e983faa0..2d994cf5 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -17,11 +17,11 @@ environment: PYTHON_VERSION: "3.8.x" PYTHON_ARCH: "64" - - TOXENV: py39-no-ext - PYTHON: "C:\\Python39-x64\\python" - PYTHONPATH: "C:\\Python39-x64" - PYTHON_VERSION: "3.9.x" - PYTHON_ARCH: "64" + # - TOXENV: py39-no-ext + # PYTHON: "C:\\Python39-x64\\python" + # PYTHONPATH: "C:\\Python39-x64" + # PYTHON_VERSION: "3.9.x" + # PYTHON_ARCH: "64" init: SET "PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%" diff --git a/docs/sanic/testing.rst b/docs/sanic/testing.rst index 0cf3ff40..b65bf4a2 100644 --- a/docs/sanic/testing.rst +++ b/docs/sanic/testing.rst @@ -58,10 +58,6 @@ More information about the available arguments to `httpx` can be found [in the documentation for `httpx `_. -Additionally, Sanic has an asynchronous testing client. The difference is that the async client will not stand up an -instance of your application, but will instead reach inside it using ASGI. All listeners and middleware are still -executed. - .. code-block:: python @pytest.mark.asyncio async def test_index_returns_200(): diff --git a/examples/run_asgi.py b/examples/run_asgi.py index e54d5d5d..39989296 100644 --- a/examples/run_asgi.py +++ b/examples/run_asgi.py @@ -10,6 +10,7 @@ from pathlib import Path from sanic import Sanic, response + app = Sanic(__name__) @@ -42,7 +43,9 @@ async def handler_file(request): @app.route("/file_stream") async def handler_file_stream(request): - return await response.file_stream(Path("../") / "setup.py", chunk_size=1024) + return await response.file_stream( + Path("../") / "setup.py", chunk_size=1024 + ) @app.route("/stream", stream=True) diff --git a/sanic/app.py b/sanic/app.py index 047879ca..15a0d18e 100644 --- a/sanic/app.py +++ b/sanic/app.py @@ -714,28 +714,6 @@ class Sanic: self._blueprint_order.append(blueprint) blueprint.register(self, options) - def register_blueprint(self, *args, **kwargs): - """ - Proxy method provided for invoking the :func:`blueprint` method - - .. note:: - To be deprecated in 1.0. Use :func:`blueprint` instead. - - :param args: Blueprint object or (list, tuple) thereof - :param kwargs: option dictionary with blueprint defaults - :return: None - """ - - if self.debug: - warnings.simplefilter("default") - warnings.warn( - "Use of register_blueprint will be deprecated in " - "version 1.0. Please use the blueprint method" - " instead", - DeprecationWarning, - ) - return self.blueprint(*args, **kwargs) - def url_for(self, view_name: str, **kwargs): r"""Build a URL based on a view name and the values provided. @@ -1026,7 +1004,6 @@ class Sanic: workers: int = 1, protocol: Optional[Type[Protocol]] = None, backlog: int = 100, - stop_event: Any = None, register_sys_signals: bool = True, access_log: Optional[bool] = None, unix: Optional[str] = None, @@ -1056,9 +1033,6 @@ class Sanic: :param backlog: a number of unaccepted connections that the system will allow before refusing new connections :type backlog: int - :param stop_event: event to be triggered - before stopping the app - deprecated - :type stop_event: None :param register_sys_signals: Register SIG* events :type register_sys_signals: bool :param access_log: Enables writing access logs (slows server) @@ -1086,13 +1060,6 @@ class Sanic: protocol = ( WebSocketProtocol if self.websocket_enabled else HttpProtocol ) - if stop_event is not None: - if debug: - warnings.simplefilter("default") - warnings.warn( - "stop_event will be removed from future versions.", - DeprecationWarning, - ) # if access_log is passed explicitly change config.ACCESS_LOG if access_log is not None: self.config.ACCESS_LOG = access_log @@ -1149,7 +1116,6 @@ class Sanic: sock: Optional[socket] = None, protocol: Type[Protocol] = None, backlog: int = 100, - stop_event: Any = None, access_log: Optional[bool] = None, unix: Optional[str] = None, return_asyncio_server=False, @@ -1182,9 +1148,6 @@ class Sanic: :param backlog: a number of unaccepted connections that the system will allow before refusing new connections :type backlog: int - :param stop_event: event to be triggered - before stopping the app - deprecated - :type stop_event: None :param access_log: Enables writing access logs (slows server) :type access_log: bool :param return_asyncio_server: flag that defines whether there's a need @@ -1204,13 +1167,6 @@ class Sanic: protocol = ( WebSocketProtocol if self.websocket_enabled else HttpProtocol ) - if stop_event is not None: - if debug: - warnings.simplefilter("default") - warnings.warn( - "stop_event will be removed from future versions.", - DeprecationWarning, - ) # if access_log is passed explicitly change config.ACCESS_LOG if access_log is not None: self.config.ACCESS_LOG = access_log @@ -1292,7 +1248,6 @@ class Sanic: loop=None, protocol=HttpProtocol, backlog=100, - stop_event=None, register_sys_signals=True, run_async=False, auto_reload=False, @@ -1307,13 +1262,6 @@ class Sanic: context = create_default_context(purpose=Purpose.CLIENT_AUTH) context.load_cert_chain(cert, keyfile=key) ssl = context - if stop_event is not None: - if debug: - warnings.simplefilter("default") - warnings.warn( - "stop_event will be removed from future versions.", - DeprecationWarning, - ) if self.config.PROXIES_COUNT and self.config.PROXIES_COUNT < 0: raise ValueError( "PROXIES_COUNT cannot be negative. " diff --git a/sanic/request.py b/sanic/request.py index 0330e121..d81702a4 100644 --- a/sanic/request.py +++ b/sanic/request.py @@ -136,15 +136,18 @@ class Request: return f"<{class_name}: {self.method} {self.path}>" def body_init(self): - """.. deprecated:: 20.3""" + """.. deprecated:: 20.3 + To be removed in 21.3""" self.body = [] def body_push(self, data): - """.. deprecated:: 20.3""" + """.. deprecated:: 20.3 + To be removed in 21.3""" self.body.append(data) def body_finish(self): - """.. deprecated:: 20.3""" + """.. deprecated:: 20.3 + To be removed in 21.3""" self.body = b"".join(self.body) async def receive_body(self): diff --git a/sanic/response.py b/sanic/response.py index 9841bb2d..9717b631 100644 --- a/sanic/response.py +++ b/sanic/response.py @@ -1,5 +1,3 @@ -import warnings - from functools import partial from mimetypes import guess_type from os import path @@ -26,6 +24,8 @@ class BaseHTTPResponse: self.asgi = False def _encode_body(self, data): + if data is None: + return b"" return data.encode() if hasattr(data, "encode") else data def _parse_headers(self): @@ -45,7 +45,7 @@ class BaseHTTPResponse: body=b"", ): """.. deprecated:: 20.3: - This function is not public API and will be removed.""" + This function is not public API and will be removed in 21.3.""" # self.headers get priority over content_type if self.content_type and "Content-Type" not in self.headers: @@ -149,22 +149,15 @@ class HTTPResponse(BaseHTTPResponse): status=200, headers=None, content_type=None, - body_bytes=b"", ): super().__init__() self.content_type = content_type - self.body = body_bytes if body is None else self._encode_body(body) + self.body = self._encode_body(body) self.status = status self.headers = Header(headers or {}) self._cookies = None - if body_bytes: - warnings.warn( - "Parameter `body_bytes` is deprecated, use `body` instead", - DeprecationWarning, - ) - def output(self, version="1.1", keep_alive=False, keep_alive_timeout=None): body = b"" if has_message_body(self.status): @@ -228,20 +221,10 @@ def text( :param content_type: the content type (string) of the response """ if not isinstance(body, str): - warnings.warn( - "Types other than str will be deprecated in future versions for" - f" response.text, got type {type(body).__name__})", - DeprecationWarning, + raise TypeError( + f"Bad body type. Expected str, got {type(body).__name__})" ) - # Type conversions are deprecated and quite b0rked but still supported for - # text() until applications get fixed. This try-except should be removed. - try: - # Avoid repr(body).encode() b0rkage for body that is already encoded. - # memoryview used only to test bytes-ishness. - with memoryview(body): - pass - except TypeError: - body = f"{body}" # no-op if body is already str + return HTTPResponse( body, status=status, headers=headers, content_type=content_type ) diff --git a/setup.py b/setup.py index a0e4bf1e..fc19e20a 100644 --- a/setup.py +++ b/setup.py @@ -5,6 +5,7 @@ import codecs import os import re import sys + from distutils.util import strtobool from setuptools import setup @@ -24,6 +25,7 @@ class PyTest(TestCommand): def run_tests(self): import shlex + import pytest errno = pytest.main(shlex.split(self.pytest_args)) @@ -38,7 +40,9 @@ def open_local(paths, mode="r", encoding="utf8"): with open_local(["sanic", "__version__.py"], encoding="latin1") as fp: try: - version = re.findall(r"^__version__ = \"([^']+)\"\r?$", fp.read(), re.M)[0] + version = re.findall( + r"^__version__ = \"([^']+)\"\r?$", fp.read(), re.M + )[0] except IndexError: raise RuntimeError("Unable to determine version.") @@ -72,7 +76,9 @@ setup_kwargs = { "entry_points": {"console_scripts": ["sanic = sanic.__main__:main"]}, } -env_dependency = '; sys_platform != "win32" ' 'and implementation_name == "cpython"' +env_dependency = ( + '; sys_platform != "win32" ' 'and implementation_name == "cpython"' +) ujson = "ujson>=1.35" + env_dependency uvloop = "uvloop>=0.5.3" + env_dependency @@ -89,9 +95,9 @@ requirements = [ tests_require = [ "pytest==5.2.1", "multidict>=5.0,<6.0", - "gunicorn", + "gunicorn==20.0.4", "pytest-cov", - "httpcore==0.3.0", + "httpcore==0.11.*", "beautifulsoup4", uvloop, ujson, diff --git a/tests/test_blueprints.py b/tests/test_blueprints.py index 60bc8221..a8fb9d87 100644 --- a/tests/test_blueprints.py +++ b/tests/test_blueprints.py @@ -825,21 +825,6 @@ def test_duplicate_blueprint(app): ) -@pytest.mark.parametrize("debug", [True, False, None]) -def test_register_blueprint(app, debug): - bp = Blueprint("bp") - - app.debug = debug - with pytest.warns(DeprecationWarning) as record: - app.register_blueprint(bp) - - assert record[0].message.args[0] == ( - "Use of register_blueprint will be deprecated in " - "version 1.0. Please use the blueprint method" - " instead" - ) - - def test_strict_slashes_behavior_adoption(app): app.strict_slashes = True diff --git a/tests/test_response.py b/tests/test_response.py index 847246d3..01e7a774 100644 --- a/tests/test_response.py +++ b/tests/test_response.py @@ -41,7 +41,8 @@ def test_response_body_not_a_string(app): return text(random_num) request, response = app.test_client.get("/hello") - assert response.text == str(random_num) + assert response.status == 500 + assert b"Internal Server Error" in response.body async def sample_streaming_fn(response): @@ -624,17 +625,3 @@ def test_empty_response(app): request, response = app.test_client.get("/test") assert response.content_type is None assert response.body == b"" - - -def test_response_body_bytes_deprecated(app): - with warnings.catch_warnings(record=True) as w: - warnings.simplefilter("always") - - HTTPResponse(body_bytes=b"bytes") - - assert len(w) == 1 - assert issubclass(w[0].category, DeprecationWarning) - assert ( - "Parameter `body_bytes` is deprecated, use `body` instead" - in str(w[0].message) - ) diff --git a/tox.ini b/tox.ini index 908f45c8..49998c98 100644 --- a/tox.ini +++ b/tox.ini @@ -7,18 +7,18 @@ setenv = {py36,py37,py38,py39,pyNightly}-no-ext: SANIC_NO_UJSON=1 {py36,py37,py38,py39,pyNightly}-no-ext: SANIC_NO_UVLOOP=1 deps = - coverage + coverage==5.3 pytest==5.2.1 pytest-cov pytest-sanic pytest-sugar pytest-benchmark pytest-dependency - httpcore==0.3.0 + httpcore==0.11.* httpx==0.15.4 - chardet<=2.3.0 + chardet==3.* beautifulsoup4 - gunicorn + gunicorn==20.0.4 uvicorn websockets>=8.1,<9.0 commands = @@ -76,7 +76,7 @@ deps = recommonmark>=0.5.0 docutils pygments - gunicorn + gunicorn==20.0.4 commands = make docs-test