From 9c576c74db04754dd2907b7c7ef3f83bb29c3518 Mon Sep 17 00:00:00 2001 From: Xavier Petit Date: Fri, 19 Nov 2021 03:28:05 -0300 Subject: [PATCH 01/11] Fix typehints in route shorthand methods (#2317) * Fix typehints in route shorthand methods Change typehint of the version variable in RouteMixin to allow int string and float types values Resolves #2314 * fix lint error in version variable --- sanic/mixins/routes.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/sanic/mixins/routes.py b/sanic/mixins/routes.py index 7139cd3c..a543a055 100644 --- a/sanic/mixins/routes.py +++ b/sanic/mixins/routes.py @@ -191,7 +191,7 @@ class RouteMixin: methods: Iterable[str] = frozenset({"GET"}), host: Optional[str] = None, strict_slashes: Optional[bool] = None, - version: Optional[int] = None, + version: Optional[Union[int, str, float]] = None, name: Optional[str] = None, stream: bool = False, version_prefix: str = "/v", @@ -256,7 +256,7 @@ class RouteMixin: uri: str, host: Optional[str] = None, strict_slashes: Optional[bool] = None, - version: Optional[int] = None, + version: Optional[Union[int, str, float]] = None, name: Optional[str] = None, ignore_body: bool = True, version_prefix: str = "/v", @@ -293,7 +293,7 @@ class RouteMixin: host: Optional[str] = None, strict_slashes: Optional[bool] = None, stream: bool = False, - version: Optional[int] = None, + version: Optional[Union[int, str, float]] = None, name: Optional[str] = None, version_prefix: str = "/v", error_format: Optional[str] = None, @@ -329,7 +329,7 @@ class RouteMixin: host: Optional[str] = None, strict_slashes: Optional[bool] = None, stream: bool = False, - version: Optional[int] = None, + version: Optional[Union[int, str, float]] = None, name: Optional[str] = None, version_prefix: str = "/v", error_format: Optional[str] = None, @@ -364,7 +364,7 @@ class RouteMixin: uri: str, host: Optional[str] = None, strict_slashes: Optional[bool] = None, - version: Optional[int] = None, + version: Optional[Union[int, str, float]] = None, name: Optional[str] = None, ignore_body: bool = True, version_prefix: str = "/v", @@ -408,7 +408,7 @@ class RouteMixin: uri: str, host: Optional[str] = None, strict_slashes: Optional[bool] = None, - version: Optional[int] = None, + version: Optional[Union[int, str, float]] = None, name: Optional[str] = None, ignore_body: bool = True, version_prefix: str = "/v", @@ -453,7 +453,7 @@ class RouteMixin: host: Optional[str] = None, strict_slashes: Optional[bool] = None, stream=False, - version: Optional[int] = None, + version: Optional[Union[int, str, float]] = None, name: Optional[str] = None, version_prefix: str = "/v", error_format: Optional[str] = None, @@ -498,7 +498,7 @@ class RouteMixin: uri: str, host: Optional[str] = None, strict_slashes: Optional[bool] = None, - version: Optional[int] = None, + version: Optional[Union[int, str, float]] = None, name: Optional[str] = None, ignore_body: bool = True, version_prefix: str = "/v", @@ -535,7 +535,7 @@ class RouteMixin: host: Optional[str] = None, strict_slashes: Optional[bool] = None, subprotocols: Optional[List[str]] = None, - version: Optional[int] = None, + version: Optional[Union[int, str, float]] = None, name: Optional[str] = None, apply: bool = True, version_prefix: str = "/v", @@ -576,7 +576,7 @@ class RouteMixin: host: Optional[str] = None, strict_slashes: Optional[bool] = None, subprotocols=None, - version: Optional[int] = None, + version: Optional[Union[int, str, float]] = None, name: Optional[str] = None, version_prefix: str = "/v", error_format: Optional[str] = None, From 722a6db8d91604e7ef0952dcd14832f796a1d48b Mon Sep 17 00:00:00 2001 From: Hussein Mohamed <58296855+whos4n3@users.noreply.github.com> Date: Fri, 19 Nov 2021 06:50:02 +0000 Subject: [PATCH 02/11] Dispatch http.lifecyle.response from exception handler (#2299) --- sanic/app.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sanic/app.py b/sanic/app.py index c801cd3d..6861bca0 100644 --- a/sanic/app.py +++ b/sanic/app.py @@ -775,6 +775,14 @@ class Sanic(BaseSanic, metaclass=TouchUpMeta): if request.stream: response = request.stream.response if isinstance(response, BaseHTTPResponse): + await self.dispatch( + "http.lifecycle.response", + inline=True, + context={ + "request": request, + "response": response, + }, + ) await response.send(end_stream=True) else: raise ServerError( From dfd33dd63d061d411fc6a28b1ae3366ffabcde3d Mon Sep 17 00:00:00 2001 From: Adam Hopkins Date: Sun, 21 Nov 2021 10:27:23 +0200 Subject: [PATCH 03/11] Update README.rst --- README.rst | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.rst b/README.rst index c6616f16..2a11ba8d 100644 --- a/README.rst +++ b/README.rst @@ -11,7 +11,7 @@ Sanic | Build fast. Run fast. :stub-columns: 1 * - Build - - | |Py39Test| |Py38Test| |Py37Test| |Codecov| + - | |Py39Test| |Py38Test| |Py37Test| * - Docs - | |UserGuide| |Documentation| * - Package @@ -27,8 +27,6 @@ Sanic | Build fast. Run fast. :target: https://community.sanicframework.org/ .. |Discord| image:: https://img.shields.io/discord/812221182594121728?logo=discord :target: https://discord.gg/FARQzAEMAA -.. |Codecov| image:: https://codecov.io/gh/sanic-org/sanic/branch/master/graph/badge.svg - :target: https://codecov.io/gh/sanic-org/sanic .. |Py39Test| image:: https://github.com/sanic-org/sanic/actions/workflows/pr-python39.yml/badge.svg?branch=main :target: https://github.com/sanic-org/sanic/actions/workflows/pr-python39.yml .. |Py38Test| image:: https://github.com/sanic-org/sanic/actions/workflows/pr-python38.yml/badge.svg?branch=main From 65e28b8c22e081333737572af57655a15743941c Mon Sep 17 00:00:00 2001 From: Adam Hopkins Date: Sun, 21 Nov 2021 15:13:34 +0200 Subject: [PATCH 04/11] Mergeback (#2319) * Mergeback From 2c03eee32948aa7d0c2f156244c25e22541f9e21 Mon Sep 17 00:00:00 2001 From: Adam Hopkins Date: Tue, 23 Nov 2021 15:53:46 +0200 Subject: [PATCH 05/11] Update tests to not run all the time (#2311) --- .github/workflows/codeql-analysis.yml | 1 + .github/workflows/coverage.yml | 26 +++++++++++++------------- .github/workflows/pr-bandit.yml | 1 + .github/workflows/pr-docs.yml | 1 + .github/workflows/pr-linter.yml | 1 + .github/workflows/pr-python310.yml | 7 +------ .github/workflows/pr-python37.yml | 7 +------ .github/workflows/pr-python38.yml | 7 +------ .github/workflows/pr-python39.yml | 7 +------ .github/workflows/pr-type-check.yml | 1 + .github/workflows/pr-windows.yml | 1 + 11 files changed, 23 insertions(+), 37 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 18415b68..6f379006 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -5,6 +5,7 @@ on: branches: [ main ] pull_request: branches: [ main ] + types: [review_requested, ready_for_review] schedule: - cron: '25 16 * * 0' diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 56a98398..4293ae74 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -1,17 +1,17 @@ name: Coverage check -# on: -# push: -# branches: -# - main -# tags: -# - "!*" # Do not execute on tags -# paths: -# - sanic/* -# - tests/* -# pull_request: -# paths: -# - "!*.MD" -on: [push, pull_request] +on: + push: + branches: + - main + tags: + - "!*" # Do not execute on tags + paths: + - sanic/* + - tests/* + pull_request: + paths: + - "!*.MD" + types: [review_requested, ready_for_review] jobs: test: runs-on: ${{ matrix.os }} diff --git a/.github/workflows/pr-bandit.yml b/.github/workflows/pr-bandit.yml index c90514e8..97aade87 100644 --- a/.github/workflows/pr-bandit.yml +++ b/.github/workflows/pr-bandit.yml @@ -3,6 +3,7 @@ on: pull_request: branches: - main + types: [review_requested, ready_for_review] jobs: bandit: diff --git a/.github/workflows/pr-docs.yml b/.github/workflows/pr-docs.yml index 1a6871c2..2d01315a 100644 --- a/.github/workflows/pr-docs.yml +++ b/.github/workflows/pr-docs.yml @@ -3,6 +3,7 @@ on: pull_request: branches: - main + types: [review_requested, ready_for_review] jobs: docsLinter: diff --git a/.github/workflows/pr-linter.yml b/.github/workflows/pr-linter.yml index 6165a988..83f7e458 100644 --- a/.github/workflows/pr-linter.yml +++ b/.github/workflows/pr-linter.yml @@ -3,6 +3,7 @@ on: pull_request: branches: - main + types: [review_requested, ready_for_review] jobs: linter: diff --git a/.github/workflows/pr-python310.yml b/.github/workflows/pr-python310.yml index 713f6495..327672a9 100644 --- a/.github/workflows/pr-python310.yml +++ b/.github/workflows/pr-python310.yml @@ -3,12 +3,7 @@ on: pull_request: branches: - main - push: - branches: - - main - paths: - - sanic/* - - tests/* + types: [review_requested, ready_for_review] jobs: testPy39: diff --git a/.github/workflows/pr-python37.yml b/.github/workflows/pr-python37.yml index 485f2592..3fb65117 100644 --- a/.github/workflows/pr-python37.yml +++ b/.github/workflows/pr-python37.yml @@ -3,12 +3,7 @@ on: pull_request: branches: - main - push: - branches: - - main - paths: - - sanic/* - - tests/* + types: [review_requested, ready_for_review] jobs: testPy37: diff --git a/.github/workflows/pr-python38.yml b/.github/workflows/pr-python38.yml index 8c46b86b..f8bcb062 100644 --- a/.github/workflows/pr-python38.yml +++ b/.github/workflows/pr-python38.yml @@ -3,12 +3,7 @@ on: pull_request: branches: - main - push: - branches: - - main - paths: - - sanic/* - - tests/* + types: [review_requested, ready_for_review] jobs: testPy38: diff --git a/.github/workflows/pr-python39.yml b/.github/workflows/pr-python39.yml index cdccf0d2..3e1967bd 100644 --- a/.github/workflows/pr-python39.yml +++ b/.github/workflows/pr-python39.yml @@ -3,12 +3,7 @@ on: pull_request: branches: - main - push: - branches: - - main - paths: - - sanic/* - - tests/* + types: [review_requested, ready_for_review] jobs: testPy39: diff --git a/.github/workflows/pr-type-check.yml b/.github/workflows/pr-type-check.yml index 80d8d9fa..73cfba90 100644 --- a/.github/workflows/pr-type-check.yml +++ b/.github/workflows/pr-type-check.yml @@ -3,6 +3,7 @@ on: pull_request: branches: - main + types: [review_requested, ready_for_review] jobs: typeChecking: diff --git a/.github/workflows/pr-windows.yml b/.github/workflows/pr-windows.yml index 73e29d4f..40ecb020 100644 --- a/.github/workflows/pr-windows.yml +++ b/.github/workflows/pr-windows.yml @@ -3,6 +3,7 @@ on: pull_request: branches: - main + types: [review_requested, ready_for_review] jobs: testsOnWindows: From 55c36e0240dfeb03deccdeb5a53ca7fcfa728bff Mon Sep 17 00:00:00 2001 From: Adam Hopkins Date: Tue, 23 Nov 2021 23:00:25 +0200 Subject: [PATCH 06/11] Fix examples to work as expected (#2305) * Fix examples to work as expected * Clean up examples * Update worker test * Merge in from main and cleanup example --- examples/add_task_sanic.py | 6 ++- examples/amending_request_object.py | 27 +++++----- examples/authorized_sanic.py | 12 +++-- .../blueprint_middlware_execution_order.py | 50 ++++++++++-------- examples/blueprints.py | 1 + examples/delayed_response.py | 5 +- examples/exception_monitoring.py | 13 ++--- examples/{simple_server.py => hello_world.py} | 0 examples/http_redirect.py | 38 +++++++++++--- examples/limit_concurrency.py | 22 ++++---- examples/log_request_id.py | 13 +++-- examples/logdna_example.py | 21 ++++---- examples/run_asgi.py | 12 ++--- examples/run_async.py | 19 +++++-- examples/run_async_advanced.py | 33 ++++++++---- examples/simple_async_view.py | 29 +++++------ examples/try_everything.py | 51 +++++++++++++------ examples/unix_socket.py | 18 ++++--- examples/url_for_example.py | 17 ++++--- examples/versioned_blueprint_group.py | 6 ++- examples/websocket.py | 18 ++++--- sanic/mixins/routes.py | 22 ++++---- sanic/models/futures.py | 2 +- tests/test_worker.py | 6 +-- 24 files changed, 267 insertions(+), 174 deletions(-) rename examples/{simple_server.py => hello_world.py} (100%) diff --git a/examples/add_task_sanic.py b/examples/add_task_sanic.py index 52b4e6bb..ece26433 100644 --- a/examples/add_task_sanic.py +++ b/examples/add_task_sanic.py @@ -4,12 +4,14 @@ import asyncio from sanic import Sanic -app = Sanic() + +app = Sanic(__name__) async def notify_server_started_after_five_seconds(): await asyncio.sleep(5) - print('Server successfully started!') + print("Server successfully started!") + app.add_task(notify_server_started_after_five_seconds()) diff --git a/examples/amending_request_object.py b/examples/amending_request_object.py index 55d889f7..7fe25bdd 100644 --- a/examples/amending_request_object.py +++ b/examples/amending_request_object.py @@ -1,30 +1,29 @@ -from sanic import Sanic -from sanic.response import text from random import randint -app = Sanic() +from sanic import Sanic +from sanic.response import text -@app.middleware('request') +app = Sanic(__name__) + + +@app.middleware("request") def append_request(request): - # Add new key with random value - request['num'] = randint(0, 100) + request.ctx.num = randint(0, 100) -@app.get('/pop') +@app.get("/pop") def pop_handler(request): - # Pop key from request object - num = request.pop('num') - return text(num) + return text(request.ctx.num) -@app.get('/key_exist') +@app.get("/key_exist") def key_exist_handler(request): # Check the key is exist or not - if 'num' in request: - return text('num exist in request') + if hasattr(request.ctx, "num"): + return text("num exist in request") - return text('num does not exist in reqeust') + return text("num does not exist in reqeust") app.run(host="0.0.0.0", port=8000, debug=True) diff --git a/examples/authorized_sanic.py b/examples/authorized_sanic.py index 7b5b7501..33e54a4b 100644 --- a/examples/authorized_sanic.py +++ b/examples/authorized_sanic.py @@ -1,10 +1,12 @@ # -*- coding: utf-8 -*- -from sanic import Sanic from functools import wraps + +from sanic import Sanic from sanic.response import json -app = Sanic() + +app = Sanic(__name__) def check_request_for_authorization_status(request): @@ -27,14 +29,16 @@ def authorized(f): return response else: # the user is not authorized. - return json({'status': 'not_authorized'}, 403) + return json({"status": "not_authorized"}, 403) + return decorated_function @app.route("/") @authorized async def test(request): - return json({'status': 'authorized'}) + return json({"status": "authorized"}) + if __name__ == "__main__": app.run(host="0.0.0.0", port=8000) diff --git a/examples/blueprint_middlware_execution_order.py b/examples/blueprint_middlware_execution_order.py index 38fc4cb1..e179c36d 100644 --- a/examples/blueprint_middlware_execution_order.py +++ b/examples/blueprint_middlware_execution_order.py @@ -1,43 +1,53 @@ -from sanic import Sanic, Blueprint +from sanic import Blueprint, Sanic from sanic.response import text -''' -Demonstrates that blueprint request middleware are executed in the order they + + +""" +Demonstrates that blueprint request middleware are executed in the order they are added. And blueprint response middleware are executed in _reverse_ order. On a valid request, it should print "1 2 3 6 5 4" to terminal -''' +""" app = Sanic(__name__) -bp = Blueprint("bp_"+__name__) +bp = Blueprint("bp_" + __name__) -@bp.middleware('request') + +@bp.on_request def request_middleware_1(request): - print('1') + print("1") -@bp.middleware('request') + +@bp.on_request def request_middleware_2(request): - print('2') + print("2") -@bp.middleware('request') + +@bp.on_request def request_middleware_3(request): - print('3') + print("3") -@bp.middleware('response') + +@bp.on_response def resp_middleware_4(request, response): - print('4') + print("4") -@bp.middleware('response') + +@bp.on_response def resp_middleware_5(request, response): - print('5') + print("5") -@bp.middleware('response') + +@bp.on_response def resp_middleware_6(request, response): - print('6') + print("6") -@bp.route('/') + +@bp.route("/") def pop_handler(request): - return text('hello world') + return text("hello world") -app.blueprint(bp, url_prefix='/bp') + +app.blueprint(bp, url_prefix="/bp") app.run(host="0.0.0.0", port=8000, debug=True, auto_reload=False) diff --git a/examples/blueprints.py b/examples/blueprints.py index 643093f6..62340a0d 100644 --- a/examples/blueprints.py +++ b/examples/blueprints.py @@ -1,6 +1,7 @@ from sanic import Blueprint, Sanic from sanic.response import file, json + app = Sanic(__name__) blueprint = Blueprint("name", url_prefix="/my_blueprint") blueprint2 = Blueprint("name2", url_prefix="/my_blueprint2") diff --git a/examples/delayed_response.py b/examples/delayed_response.py index 4105edba..5923d10a 100644 --- a/examples/delayed_response.py +++ b/examples/delayed_response.py @@ -2,17 +2,20 @@ from asyncio import sleep from sanic import Sanic, response + app = Sanic(__name__, strict_slashes=True) + @app.get("/") async def handler(request): return response.redirect("/sleep/3") + @app.get("/sleep/") async def handler2(request, t=0.3): await sleep(t) return response.text(f"Slept {t:.1f} seconds.\n") -if __name__ == '__main__': +if __name__ == "__main__": app.run(host="0.0.0.0", port=8000) diff --git a/examples/exception_monitoring.py b/examples/exception_monitoring.py index 02a13e7d..3d853d32 100644 --- a/examples/exception_monitoring.py +++ b/examples/exception_monitoring.py @@ -7,8 +7,10 @@ and pass in an instance of it when we create our Sanic instance. Inside this class' default handler, we can do anything including sending exceptions to an external service. """ -from sanic.handlers import ErrorHandler from sanic.exceptions import SanicException +from sanic.handlers import ErrorHandler + + """ Imports and code relevant for our CustomHandler class (Ordinarily this would be in a separate file) @@ -16,7 +18,6 @@ Imports and code relevant for our CustomHandler class class CustomHandler(ErrorHandler): - def default(self, request, exception): # Here, we have access to the exception object # and can do anything with it (log, send to external service, etc) @@ -38,17 +39,17 @@ server's error_handler to an instance of our CustomHandler from sanic import Sanic -app = Sanic(__name__) handler = CustomHandler() -app.error_handler = handler +app = Sanic(__name__, error_handler=handler) @app.route("/") async def test(request): # Here, something occurs which causes an unexpected exception # This exception will flow to our custom handler. - raise SanicException('You Broke It!') + raise SanicException("You Broke It!") -if __name__ == '__main__': + +if __name__ == "__main__": app.run(host="0.0.0.0", port=8000, debug=True) diff --git a/examples/simple_server.py b/examples/hello_world.py similarity index 100% rename from examples/simple_server.py rename to examples/hello_world.py diff --git a/examples/http_redirect.py b/examples/http_redirect.py index 2e38eb92..50a79d81 100644 --- a/examples/http_redirect.py +++ b/examples/http_redirect.py @@ -1,4 +1,6 @@ from sanic import Sanic, response, text +from sanic.handlers import ErrorHandler +from sanic.server.async_server import AsyncioServer HTTP_PORT = 9999 @@ -32,20 +34,40 @@ def proxy(request, path): return response.redirect(url) -@https.listener("main_process_start") +@https.main_process_start async def start(app, _): - global http - app.http_server = await http.create_server( + http_server = await http.create_server( port=HTTP_PORT, return_asyncio_server=True ) - app.http_server.after_start() + app.add_task(runner(http, http_server)) + app.ctx.http_server = http_server + app.ctx.http = http -@https.listener("main_process_stop") +@https.main_process_stop async def stop(app, _): - app.http_server.before_stop() - await app.http_server.close() - app.http_server.after_stop() + await app.ctx.http_server.before_stop() + await app.ctx.http_server.close() + for connection in app.ctx.http_server.connections: + connection.close_if_idle() + await app.ctx.http_server.after_stop() + app.ctx.http = False + + +async def runner(app: Sanic, app_server: AsyncioServer): + app.is_running = True + try: + app.signalize() + app.finalize() + ErrorHandler.finalize(app.error_handler) + app_server.init = True + + await app_server.before_start() + await app_server.after_start() + await app_server.serve_forever() + finally: + app.is_running = False + app.is_stopping = True https.run(port=HTTPS_PORT, debug=True) diff --git a/examples/limit_concurrency.py b/examples/limit_concurrency.py index f6b4b01a..429a312b 100644 --- a/examples/limit_concurrency.py +++ b/examples/limit_concurrency.py @@ -1,26 +1,30 @@ +import asyncio + +import httpx + from sanic import Sanic from sanic.response import json -import asyncio -import aiohttp app = Sanic(__name__) sem = None -@app.listener('before_server_start') -def init(sanic, loop): +@app.before_server_start +def init(sanic, _): global sem concurrency_per_worker = 4 - sem = asyncio.Semaphore(concurrency_per_worker, loop=loop) + sem = asyncio.Semaphore(concurrency_per_worker) + async def bounded_fetch(session, url): """ Use session object to perform 'get' request on url """ - async with sem, session.get(url) as response: - return await response.json() + async with sem: + response = await session.get(url) + return response.json() @app.route("/") @@ -28,9 +32,9 @@ async def test(request): """ Download and serve example JSON """ - url = "https://api.github.com/repos/channelcat/sanic" + url = "https://api.github.com/repos/sanic-org/sanic" - async with aiohttp.ClientSession() as session: + async with httpx.AsyncClient() as session: response = await bounded_fetch(session, url) return json(response) diff --git a/examples/log_request_id.py b/examples/log_request_id.py index 27d987bc..c0d2d6f9 100644 --- a/examples/log_request_id.py +++ b/examples/log_request_id.py @@ -1,6 +1,6 @@ import logging -import aiotask_context as context +from contextvars import ContextVar from sanic import Sanic, response @@ -11,8 +11,8 @@ log = logging.getLogger(__name__) class RequestIdFilter(logging.Filter): def filter(self, record): try: - record.request_id = context.get("X-Request-ID") - except ValueError: + record.request_id = app.ctx.request_id.get(None) or "n/a" + except AttributeError: record.request_id = "n/a" return True @@ -49,8 +49,7 @@ app = Sanic(__name__, log_config=LOG_SETTINGS) @app.on_request async def set_request_id(request): - request_id = request.id - context.set("X-Request-ID", request_id) + request.app.ctx.request_id.set(request.id) log.info(f"Setting {request.id=}") @@ -61,14 +60,14 @@ async def set_request_header(request, response): @app.route("/") async def test(request): - log.debug("X-Request-ID: %s", context.get("X-Request-ID")) + log.debug("X-Request-ID: %s", request.id) log.info("Hello from test!") return response.json({"test": True}) @app.before_server_start def setup(app, loop): - loop.set_task_factory(context.task_factory) + app.ctx.request_id = ContextVar("request_id") if __name__ == "__main__": diff --git a/examples/logdna_example.py b/examples/logdna_example.py index da38f404..01236d98 100644 --- a/examples/logdna_example.py +++ b/examples/logdna_example.py @@ -1,5 +1,6 @@ import logging import socket + from os import getenv from platform import node from uuid import getnode as get_mac @@ -7,10 +8,11 @@ from uuid import getnode as get_mac from logdna import LogDNAHandler from sanic import Sanic -from sanic.response import json from sanic.request import Request +from sanic.response import json -log = logging.getLogger('logdna') + +log = logging.getLogger("logdna") log.setLevel(logging.INFO) @@ -30,10 +32,12 @@ logdna_options = { "index_meta": True, "hostname": node(), "ip": get_my_ip_address(), - "mac": get_mac_address() + "mac": get_mac_address(), } -logdna_handler = LogDNAHandler(getenv("LOGDNA_API_KEY"), options=logdna_options) +logdna_handler = LogDNAHandler( + getenv("LOGDNA_API_KEY"), options=logdna_options +) logdna = logging.getLogger(__name__) logdna.setLevel(logging.INFO) @@ -49,13 +53,8 @@ def log_request(request: Request): @app.route("/") def default(request): - return json({ - "response": "I was here" - }) + return json({"response": "I was here"}) if __name__ == "__main__": - app.run( - host="0.0.0.0", - port=getenv("PORT", 8080) - ) + app.run(host="0.0.0.0", port=getenv("PORT", 8080)) diff --git a/examples/run_asgi.py b/examples/run_asgi.py index d4351c17..c29c5fbb 100644 --- a/examples/run_asgi.py +++ b/examples/run_asgi.py @@ -59,31 +59,31 @@ async def handler_stream(request): return response.stream(body) -@app.listener("before_server_start") +@app.before_server_start async def listener_before_server_start(*args, **kwargs): print("before_server_start") -@app.listener("after_server_start") +@app.after_server_start async def listener_after_server_start(*args, **kwargs): print("after_server_start") -@app.listener("before_server_stop") +@app.before_server_stop async def listener_before_server_stop(*args, **kwargs): print("before_server_stop") -@app.listener("after_server_stop") +@app.after_server_stop async def listener_after_server_stop(*args, **kwargs): print("after_server_stop") -@app.middleware("request") +@app.on_request async def print_on_request(request): print("print_on_request") -@app.middleware("response") +@app.on_response async def print_on_response(request, response): print("print_on_response") diff --git a/examples/run_async.py b/examples/run_async.py index c35da8b1..a30417d7 100644 --- a/examples/run_async.py +++ b/examples/run_async.py @@ -1,9 +1,12 @@ -from sanic import Sanic -from sanic import response -from signal import signal, SIGINT import asyncio + +from signal import SIGINT, signal + import uvloop +from sanic import Sanic, response + + app = Sanic(__name__) @@ -11,12 +14,18 @@ app = Sanic(__name__) async def test(request): return response.json({"answer": "42"}) + asyncio.set_event_loop(uvloop.new_event_loop()) -server = app.create_server(host="0.0.0.0", port=8000, return_asyncio_server=True) +server = app.create_server( + host="0.0.0.0", port=8000, return_asyncio_server=True +) loop = asyncio.get_event_loop() task = asyncio.ensure_future(server) +server = loop.run_until_complete(task) +loop.run_until_complete(server.startup()) signal(SIGINT, lambda s, f: loop.stop()) + try: loop.run_forever() -except: +finally: loop.stop() diff --git a/examples/run_async_advanced.py b/examples/run_async_advanced.py index 27f86f3f..7ea30dd7 100644 --- a/examples/run_async_advanced.py +++ b/examples/run_async_advanced.py @@ -11,9 +11,24 @@ from sanic.server import AsyncioServer app = Sanic(__name__) -@app.listener("after_server_start") -async def after_start_test(app, loop): - print("Async Server Started!") +@app.before_server_start +async def before_server_start(app, loop): + print("Async Server starting") + + +@app.after_server_start +async def after_server_start(app, loop): + print("Async Server started") + + +@app.before_server_stop +async def before_server_stop(app, loop): + print("Async Server stopping") + + +@app.after_server_stop +async def after_server_stop(app, loop): + print("Async Server stopped") @app.route("/") @@ -28,20 +43,20 @@ serv_coro = app.create_server( loop = asyncio.get_event_loop() serv_task = asyncio.ensure_future(serv_coro, loop=loop) signal(SIGINT, lambda s, f: loop.stop()) -server: AsyncioServer = loop.run_until_complete(serv_task) # type: ignore -server.startup() +server: AsyncioServer = loop.run_until_complete(serv_task) +loop.run_until_complete(server.startup()) # When using app.run(), this actually triggers before the serv_coro. # But, in this example, we are using the convenience method, even if it is # out of order. -server.before_start() -server.after_start() +loop.run_until_complete(server.before_start()) +loop.run_until_complete(server.after_start()) try: loop.run_forever() except KeyboardInterrupt: loop.stop() finally: - server.before_stop() + loop.run_until_complete(server.before_stop()) # Wait for server to close close_task = server.close() @@ -50,4 +65,4 @@ finally: # Complete all tasks on the loop for connection in server.connections: connection.close_if_idle() - server.after_stop() + loop.run_until_complete(server.after_stop()) diff --git a/examples/simple_async_view.py b/examples/simple_async_view.py index 990aa21a..4e73967c 100644 --- a/examples/simple_async_view.py +++ b/examples/simple_async_view.py @@ -1,42 +1,41 @@ from sanic import Sanic -from sanic.views import HTTPMethodView from sanic.response import text +from sanic.views import HTTPMethodView -app = Sanic('some_name') + +app = Sanic("some_name") class SimpleView(HTTPMethodView): - def get(self, request): - return text('I am get method') + return text("I am get method") def post(self, request): - return text('I am post method') + return text("I am post method") def put(self, request): - return text('I am put method') + return text("I am put method") def patch(self, request): - return text('I am patch method') + return text("I am patch method") def delete(self, request): - return text('I am delete method') + return text("I am delete method") class SimpleAsyncView(HTTPMethodView): - async def get(self, request): - return text('I am async get method') + return text("I am async get method") async def post(self, request): - return text('I am async post method') + return text("I am async post method") async def put(self, request): - return text('I am async put method') + return text("I am async put method") -app.add_route(SimpleView.as_view(), '/') -app.add_route(SimpleAsyncView.as_view(), '/async') +app.add_route(SimpleView.as_view(), "/") +app.add_route(SimpleAsyncView.as_view(), "/async") -if __name__ == '__main__': +if __name__ == "__main__": app.run(host="0.0.0.0", port=8000, debug=True) diff --git a/examples/try_everything.py b/examples/try_everything.py index a775704d..8e4a8e09 100644 --- a/examples/try_everything.py +++ b/examples/try_everything.py @@ -1,9 +1,9 @@ import os -from sanic import Sanic -from sanic.log import logger as log -from sanic import response +from sanic import Sanic, response from sanic.exceptions import ServerError +from sanic.log import logger as log + app = Sanic(__name__) @@ -13,7 +13,7 @@ async def test_async(request): return response.json({"test": True}) -@app.route("/sync", methods=['GET', 'POST']) +@app.route("/sync", methods=["GET", "POST"]) def test_sync(request): return response.json({"test": True}) @@ -31,6 +31,7 @@ def exception(request): @app.route("/await") async def test_await(request): import asyncio + await asyncio.sleep(5) return response.text("I'm feeling sleepy") @@ -42,8 +43,10 @@ async def test_file(request): @app.route("/file_stream") async def test_file_stream(request): - return await response.file_stream(os.path.abspath("setup.py"), - chunk_size=1024) + return await response.file_stream( + os.path.abspath("setup.py"), chunk_size=1024 + ) + # ----------------------------------------------- # # Exceptions @@ -52,14 +55,17 @@ async def test_file_stream(request): @app.exception(ServerError) async def test(request, exception): - return response.json({"exception": "{}".format(exception), "status": exception.status_code}, - status=exception.status_code) + return response.json( + {"exception": str(exception), "status": exception.status_code}, + status=exception.status_code, + ) # ----------------------------------------------- # # Read from request # ----------------------------------------------- # + @app.route("/json") def post_json(request): return response.json({"received": True, "message": request.json}) @@ -67,38 +73,51 @@ def post_json(request): @app.route("/form") def post_form_json(request): - return response.json({"received": True, "form_data": request.form, "test": request.form.get('test')}) + return response.json( + { + "received": True, + "form_data": request.form, + "test": request.form.get("test"), + } + ) @app.route("/query_string") def query_string(request): - return response.json({"parsed": True, "args": request.args, "url": request.url, - "query_string": request.query_string}) + return response.json( + { + "parsed": True, + "args": request.args, + "url": request.url, + "query_string": request.query_string, + } + ) # ----------------------------------------------- # # Run Server # ----------------------------------------------- # -@app.listener('before_server_start') + +@app.before_server_start def before_start(app, loop): log.info("SERVER STARTING") -@app.listener('after_server_start') +@app.after_server_start def after_start(app, loop): log.info("OH OH OH OH OHHHHHHHH") -@app.listener('before_server_stop') +@app.before_server_stop def before_stop(app, loop): log.info("SERVER STOPPING") -@app.listener('after_server_stop') +@app.after_server_stop def after_stop(app, loop): log.info("TRIED EVERYTHING") -if __name__ == '__main__': +if __name__ == "__main__": app.run(host="0.0.0.0", port=8000, debug=True) diff --git a/examples/unix_socket.py b/examples/unix_socket.py index 08e89445..a64b205d 100644 --- a/examples/unix_socket.py +++ b/examples/unix_socket.py @@ -1,7 +1,8 @@ -from sanic import Sanic -from sanic import response -import socket import os +import socket + +from sanic import Sanic, response + app = Sanic(__name__) @@ -10,14 +11,15 @@ app = Sanic(__name__) async def test(request): return response.text("OK") -if __name__ == '__main__': - server_address = './uds_socket' + +if __name__ == "__main__": + server_address = "./uds_socket" # Make sure the socket does not already exist try: - os.unlink(server_address) + os.unlink(server_address) except OSError: - if os.path.exists(server_address): - raise + if os.path.exists(server_address): + raise sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) sock.bind(server_address) app.run(sock=sock) diff --git a/examples/url_for_example.py b/examples/url_for_example.py index cb895b0c..f0d3614b 100644 --- a/examples/url_for_example.py +++ b/examples/url_for_example.py @@ -1,20 +1,21 @@ -from sanic import Sanic -from sanic import response +from sanic import Sanic, response + app = Sanic(__name__) -@app.route('/') +@app.route("/") async def index(request): # generate a URL for the endpoint `post_handler` - url = app.url_for('post_handler', post_id=5) + url = app.url_for("post_handler", post_id=5) # the URL is `/posts/5`, redirect to it return response.redirect(url) -@app.route('/posts/') +@app.route("/posts/") async def post_handler(request, post_id): - return response.text('Post - {}'.format(post_id)) - -if __name__ == '__main__': + return response.text("Post - {}".format(post_id)) + + +if __name__ == "__main__": app.run(host="0.0.0.0", port=8000, debug=True) diff --git a/examples/versioned_blueprint_group.py b/examples/versioned_blueprint_group.py index 77360f5d..56715acc 100644 --- a/examples/versioned_blueprint_group.py +++ b/examples/versioned_blueprint_group.py @@ -8,7 +8,9 @@ app = Sanic(name="blue-print-group-version-example") bp1 = Blueprint(name="ultron", url_prefix="/ultron") bp2 = Blueprint(name="vision", url_prefix="/vision", strict_slashes=None) -bpg = Blueprint.group([bp1, bp2], url_prefix="/sentient/robot", version=1, strict_slashes=True) +bpg = Blueprint.group( + bp1, bp2, url_prefix="/sentient/robot", version=1, strict_slashes=True +) @bp1.get("/name") @@ -31,5 +33,5 @@ async def bp2_revised_name(request): app.blueprint(bpg) -if __name__ == '__main__': +if __name__ == "__main__": app.run(host="0.0.0.0", port=8000) diff --git a/examples/websocket.py b/examples/websocket.py index 92f71375..7bcd2cd1 100644 --- a/examples/websocket.py +++ b/examples/websocket.py @@ -1,25 +1,27 @@ from sanic import Sanic from sanic.response import redirect + app = Sanic(__name__) -app.static('index.html', "websocket.html") +app.static("index.html", "websocket.html") -@app.route('/') + +@app.route("/") def index(request): return redirect("index.html") -@app.websocket('/feed') + +@app.websocket("/feed") async def feed(request, ws): while True: - data = 'hello!' - print('Sending: ' + data) + data = "hello!" + print("Sending: " + data) await ws.send(data) data = await ws.recv() - print('Received: ' + data) + print("Received: " + data) -if __name__ == '__main__': +if __name__ == "__main__": app.run(host="0.0.0.0", port=8000, debug=True) - diff --git a/sanic/mixins/routes.py b/sanic/mixins/routes.py index a543a055..01911e66 100644 --- a/sanic/mixins/routes.py +++ b/sanic/mixins/routes.py @@ -52,7 +52,7 @@ class RouteMixin: self, uri: str, methods: Optional[Iterable[str]] = None, - host: Optional[str] = None, + host: Optional[Union[str, List[str]]] = None, strict_slashes: Optional[bool] = None, stream: bool = False, version: Optional[Union[int, str, float]] = None, @@ -189,7 +189,7 @@ class RouteMixin: handler: RouteHandler, uri: str, methods: Iterable[str] = frozenset({"GET"}), - host: Optional[str] = None, + host: Optional[Union[str, List[str]]] = None, strict_slashes: Optional[bool] = None, version: Optional[Union[int, str, float]] = None, name: Optional[str] = None, @@ -254,7 +254,7 @@ class RouteMixin: def get( self, uri: str, - host: Optional[str] = None, + host: Optional[Union[str, List[str]]] = None, strict_slashes: Optional[bool] = None, version: Optional[Union[int, str, float]] = None, name: Optional[str] = None, @@ -290,7 +290,7 @@ class RouteMixin: def post( self, uri: str, - host: Optional[str] = None, + host: Optional[Union[str, List[str]]] = None, strict_slashes: Optional[bool] = None, stream: bool = False, version: Optional[Union[int, str, float]] = None, @@ -326,7 +326,7 @@ class RouteMixin: def put( self, uri: str, - host: Optional[str] = None, + host: Optional[Union[str, List[str]]] = None, strict_slashes: Optional[bool] = None, stream: bool = False, version: Optional[Union[int, str, float]] = None, @@ -362,7 +362,7 @@ class RouteMixin: def head( self, uri: str, - host: Optional[str] = None, + host: Optional[Union[str, List[str]]] = None, strict_slashes: Optional[bool] = None, version: Optional[Union[int, str, float]] = None, name: Optional[str] = None, @@ -406,7 +406,7 @@ class RouteMixin: def options( self, uri: str, - host: Optional[str] = None, + host: Optional[Union[str, List[str]]] = None, strict_slashes: Optional[bool] = None, version: Optional[Union[int, str, float]] = None, name: Optional[str] = None, @@ -450,7 +450,7 @@ class RouteMixin: def patch( self, uri: str, - host: Optional[str] = None, + host: Optional[Union[str, List[str]]] = None, strict_slashes: Optional[bool] = None, stream=False, version: Optional[Union[int, str, float]] = None, @@ -496,7 +496,7 @@ class RouteMixin: def delete( self, uri: str, - host: Optional[str] = None, + host: Optional[Union[str, List[str]]] = None, strict_slashes: Optional[bool] = None, version: Optional[Union[int, str, float]] = None, name: Optional[str] = None, @@ -532,7 +532,7 @@ class RouteMixin: def websocket( self, uri: str, - host: Optional[str] = None, + host: Optional[Union[str, List[str]]] = None, strict_slashes: Optional[bool] = None, subprotocols: Optional[List[str]] = None, version: Optional[Union[int, str, float]] = None, @@ -573,7 +573,7 @@ class RouteMixin: self, handler, uri: str, - host: Optional[str] = None, + host: Optional[Union[str, List[str]]] = None, strict_slashes: Optional[bool] = None, subprotocols=None, version: Optional[Union[int, str, float]] = None, diff --git a/sanic/models/futures.py b/sanic/models/futures.py index 74ee92b9..21f9c674 100644 --- a/sanic/models/futures.py +++ b/sanic/models/futures.py @@ -13,7 +13,7 @@ class FutureRoute(NamedTuple): handler: str uri: str methods: Optional[Iterable[str]] - host: str + host: Union[str, List[str]] strict_slashes: bool stream: bool version: Optional[int] diff --git a/tests/test_worker.py b/tests/test_worker.py index 1fec3b54..cdc30a05 100644 --- a/tests/test_worker.py +++ b/tests/test_worker.py @@ -21,7 +21,7 @@ def gunicorn_worker(): "gunicorn " f"--bind 127.0.0.1:{PORT} " "--worker-class sanic.worker.GunicornWorker " - "examples.simple_server:app" + "examples.hello_world:app" ) worker = subprocess.Popen(shlex.split(command)) time.sleep(2) @@ -35,7 +35,7 @@ def gunicorn_worker_with_access_logs(): "gunicorn " f"--bind 127.0.0.1:{PORT + 1} " "--worker-class sanic.worker.GunicornWorker " - "examples.simple_server:app" + "examples.hello_world:app" ) worker = subprocess.Popen(shlex.split(command), stdout=subprocess.PIPE) time.sleep(2) @@ -50,7 +50,7 @@ def gunicorn_worker_with_env_var(): f"--bind 127.0.0.1:{PORT + 2} " "--worker-class sanic.worker.GunicornWorker " "--log-level info " - "examples.simple_server:app" + "examples.hello_world:app" ) worker = subprocess.Popen(shlex.split(command), stdout=subprocess.PIPE) time.sleep(2) From a8d55e180c24578bc1a66c9a39cca6b35b8c90b5 Mon Sep 17 00:00:00 2001 From: Zhiwei Date: Thu, 2 Dec 2021 14:17:22 -0700 Subject: [PATCH 07/11] Fix Type Hint (#2322) * Fix Type Hint Union is not necessary here? My IDE pops an error for it. * Rerun workflows * empty commit --- sanic/response.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sanic/response.py b/sanic/response.py index 1f1d7fbe..1da4486a 100644 --- a/sanic/response.py +++ b/sanic/response.py @@ -101,7 +101,7 @@ class BaseHTTPResponse: async def send( self, - data: Optional[Union[AnyStr]] = None, + data: Optional[AnyStr] = None, end_stream: Optional[bool] = None, ) -> None: """ From f641830d260bfb236b15573e2cbde29a131b5fcd Mon Sep 17 00:00:00 2001 From: Clay Sweetser Date: Thu, 2 Dec 2021 17:01:20 -0500 Subject: [PATCH 08/11] Refactor environment variable hydration logic. (#2321) - Refactor environment variable hydration logic to be less nested. This allows possible future extension of the hydration logic. - Fix a spelling mistake in `load_environment_vars` docstring. Co-authored-by: Adam Hopkins --- sanic/config.py | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/sanic/config.py b/sanic/config.py index ebe1a9a6..261f608a 100644 --- a/sanic/config.py +++ b/sanic/config.py @@ -174,11 +174,11 @@ class Config(dict): def load_environment_vars(self, prefix=SANIC_PREFIX): """ - Looks for prefixed environment variables and applies - them to the configuration if present. This is called automatically when - Sanic starts up to load environment variables into config. + Looks for prefixed environment variables and applies them to the + configuration if present. This is called automatically when Sanic + starts up to load environment variables into config. - It will automatically hyrdate the following types: + It will automatically hydrate the following types: - ``int`` - ``float`` @@ -186,19 +186,18 @@ class Config(dict): Anything else will be imported as a ``str``. """ - for k, v in environ.items(): - if k.startswith(prefix): - _, config_key = k.split(prefix, 1) + for key, value in environ.items(): + if not key.startswith(prefix): + continue + + _, config_key = key.split(prefix, 1) + + for converter in (int, float, str_to_bool, str): try: - self[config_key] = int(v) + self[config_key] = converter(value) + break except ValueError: - try: - self[config_key] = float(v) - except ValueError: - try: - self[config_key] = str_to_bool(v) - except ValueError: - self[config_key] = v + pass def update_config(self, config: Union[bytes, str, dict, Any]): """ From 19f6544923faf3760d20658a0926e41d5e10b0db Mon Sep 17 00:00:00 2001 From: Adam Hopkins Date: Fri, 3 Dec 2021 13:27:16 +0200 Subject: [PATCH 09/11] Resolve potential bug in MOTD in Docker (#2331) * Resolve potential bug in MOTD in Docker --- sanic/application/motd.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sanic/application/motd.py b/sanic/application/motd.py index 27c36663..32825b12 100644 --- a/sanic/application/motd.py +++ b/sanic/application/motd.py @@ -64,8 +64,10 @@ class MOTDTTY(MOTD): self.set_variables() def set_variables(self): # no cov - fallback = (80, 24) - terminal_width = min(get_terminal_size(fallback=fallback).columns, 108) + fallback = (108, 24) + terminal_width = max( + get_terminal_size(fallback=fallback).columns, fallback[0] + ) self.max_value_width = terminal_width - fallback[0] + 36 self.key_width = 4 From cf3c205fa53c9f309a7c3315fed76a7a80c22c97 Mon Sep 17 00:00:00 2001 From: Zhiwei <43905414+ChihweiLHBird@users.noreply.github.com> Date: Sun, 5 Dec 2021 15:05:07 -0700 Subject: [PATCH 10/11] Fixed: CI Runs on Ready PRs Only (#2324) * Revert "Update tests to not run all the time (#2311)" This reverts commit 2c03eee32948aa7d0c2f156244c25e22541f9e21. * Make CI only runs on PRs that are ready * Remove CI Tasks on Push --- .github/workflows/codeql-analysis.yml | 3 ++- .github/workflows/coverage.yml | 3 ++- .github/workflows/pr-bandit.yml | 3 ++- .github/workflows/pr-docs.yml | 3 ++- .github/workflows/pr-linter.yml | 3 ++- .github/workflows/pr-python310.yml | 5 +++-- .github/workflows/pr-python37.yml | 3 ++- .github/workflows/pr-python38.yml | 3 ++- .github/workflows/pr-python39.yml | 3 ++- .github/workflows/pr-type-check.yml | 3 ++- .github/workflows/pr-windows.yml | 3 ++- 11 files changed, 23 insertions(+), 12 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 6f379006..5108c247 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -5,12 +5,13 @@ on: branches: [ main ] pull_request: branches: [ main ] - types: [review_requested, ready_for_review] + types: [opened, synchronize, reopened, ready_for_review] schedule: - cron: '25 16 * * 0' jobs: analyze: + if: github.event.pull_request.draft == false name: Analyze runs-on: ubuntu-latest diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 4293ae74..c478a961 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -11,9 +11,10 @@ on: pull_request: paths: - "!*.MD" - types: [review_requested, ready_for_review] + types: [opened, synchronize, reopened, ready_for_review] jobs: test: + if: github.event.pull_request.draft == false runs-on: ${{ matrix.os }} strategy: matrix: diff --git a/.github/workflows/pr-bandit.yml b/.github/workflows/pr-bandit.yml index 97aade87..ca91312a 100644 --- a/.github/workflows/pr-bandit.yml +++ b/.github/workflows/pr-bandit.yml @@ -3,10 +3,11 @@ on: pull_request: branches: - main - types: [review_requested, ready_for_review] + types: [opened, synchronize, reopened, ready_for_review] jobs: bandit: + if: github.event.pull_request.draft == false name: type-check-${{ matrix.config.python-version }} runs-on: ${{ matrix.os }} strategy: diff --git a/.github/workflows/pr-docs.yml b/.github/workflows/pr-docs.yml index 2d01315a..7b3c2f6e 100644 --- a/.github/workflows/pr-docs.yml +++ b/.github/workflows/pr-docs.yml @@ -3,10 +3,11 @@ on: pull_request: branches: - main - types: [review_requested, ready_for_review] + types: [opened, synchronize, reopened, ready_for_review] jobs: docsLinter: + if: github.event.pull_request.draft == false name: Lint Documentation runs-on: ubuntu-latest strategy: diff --git a/.github/workflows/pr-linter.yml b/.github/workflows/pr-linter.yml index 83f7e458..9ed45d0a 100644 --- a/.github/workflows/pr-linter.yml +++ b/.github/workflows/pr-linter.yml @@ -3,10 +3,11 @@ on: pull_request: branches: - main - types: [review_requested, ready_for_review] + types: [opened, synchronize, reopened, ready_for_review] jobs: linter: + if: github.event.pull_request.draft == false name: lint runs-on: ${{ matrix.os }} strategy: diff --git a/.github/workflows/pr-python310.yml b/.github/workflows/pr-python310.yml index 327672a9..f3f7c607 100644 --- a/.github/workflows/pr-python310.yml +++ b/.github/workflows/pr-python310.yml @@ -3,10 +3,11 @@ on: pull_request: branches: - main - types: [review_requested, ready_for_review] + types: [opened, synchronize, reopened, ready_for_review] jobs: - testPy39: + testPy310: + if: github.event.pull_request.draft == false name: ut-${{ matrix.config.tox-env }}-${{ matrix.os }} runs-on: ${{ matrix.os }} strategy: diff --git a/.github/workflows/pr-python37.yml b/.github/workflows/pr-python37.yml index 3fb65117..50f79c6e 100644 --- a/.github/workflows/pr-python37.yml +++ b/.github/workflows/pr-python37.yml @@ -3,10 +3,11 @@ on: pull_request: branches: - main - types: [review_requested, ready_for_review] + types: [opened, synchronize, reopened, ready_for_review] jobs: testPy37: + if: github.event.pull_request.draft == false name: ut-${{ matrix.config.tox-env }}-${{ matrix.os }} runs-on: ${{ matrix.os }} strategy: diff --git a/.github/workflows/pr-python38.yml b/.github/workflows/pr-python38.yml index f8bcb062..1e0b8050 100644 --- a/.github/workflows/pr-python38.yml +++ b/.github/workflows/pr-python38.yml @@ -3,10 +3,11 @@ on: pull_request: branches: - main - types: [review_requested, ready_for_review] + types: [opened, synchronize, reopened, ready_for_review] jobs: testPy38: + if: github.event.pull_request.draft == false name: ut-${{ matrix.config.tox-env }}-${{ matrix.os }} runs-on: ${{ matrix.os }} strategy: diff --git a/.github/workflows/pr-python39.yml b/.github/workflows/pr-python39.yml index 3e1967bd..1abd6bcb 100644 --- a/.github/workflows/pr-python39.yml +++ b/.github/workflows/pr-python39.yml @@ -3,10 +3,11 @@ on: pull_request: branches: - main - types: [review_requested, ready_for_review] + types: [opened, synchronize, reopened, ready_for_review] jobs: testPy39: + if: github.event.pull_request.draft == false name: ut-${{ matrix.config.tox-env }}-${{ matrix.os }} runs-on: ${{ matrix.os }} strategy: diff --git a/.github/workflows/pr-type-check.yml b/.github/workflows/pr-type-check.yml index 73cfba90..2fae03be 100644 --- a/.github/workflows/pr-type-check.yml +++ b/.github/workflows/pr-type-check.yml @@ -3,10 +3,11 @@ on: pull_request: branches: - main - types: [review_requested, ready_for_review] + types: [opened, synchronize, reopened, ready_for_review] jobs: typeChecking: + if: github.event.pull_request.draft == false name: type-check-${{ matrix.config.python-version }} runs-on: ${{ matrix.os }} strategy: diff --git a/.github/workflows/pr-windows.yml b/.github/workflows/pr-windows.yml index 40ecb020..9721b5b5 100644 --- a/.github/workflows/pr-windows.yml +++ b/.github/workflows/pr-windows.yml @@ -3,10 +3,11 @@ on: pull_request: branches: - main - types: [review_requested, ready_for_review] + types: [opened, synchronize, reopened, ready_for_review] jobs: testsOnWindows: + if: github.event.pull_request.draft == false name: ut-${{ matrix.config.tox-env }} runs-on: windows-latest strategy: From ab35121864848054c23725c24818ee926cfaad71 Mon Sep 17 00:00:00 2001 From: Kian Meng Ang Date: Mon, 6 Dec 2021 15:17:01 +0800 Subject: [PATCH 11/11] Fix typos (#2329) * Fix typos * Downgrade mistune version * Fix blueprint host param Co-authored-by: Adam Hopkins Co-authored-by: Adam Hopkins --- CHANGELOG.rst | 6 +++--- examples/amending_request_object.py | 2 +- sanic/app.py | 2 +- sanic/blueprints.py | 4 ++-- sanic/headers.py | 2 +- sanic/helpers.py | 2 +- sanic/router.py | 2 +- sanic/tls.py | 2 +- sanic/utils.py | 6 +++--- scripts/release.py | 2 +- setup.py | 1 + tests/test_request_data.py | 2 +- 12 files changed, 17 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index a9940da1..5f09fd51 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -657,7 +657,7 @@ Improved Documentation Version 20.6.0 --------------- -*Released, but unintentionally ommitting PR #1880, so was replaced by 20.6.1* +*Released, but unintentionally omitting PR #1880, so was replaced by 20.6.1* Version 20.3.0 @@ -1090,7 +1090,7 @@ Version 18.12 * Fix Range header handling for static files (#1402) * Fix the logger and make it work (#1397) * Fix type pikcle->pickle in multiprocessing test - * Fix pickling blueprints Change the string passed in the "name" section of the namedtuples in Blueprint to match the name of the Blueprint module attribute name. This allows blueprints to be pickled and unpickled, without errors, which is a requirment of running Sanic in multiprocessing mode in Windows. Added a test for pickling and unpickling blueprints Added a test for pickling and unpickling sanic itself Added a test for enabling multiprocessing on an app with a blueprint (only useful to catch this bug if the tests are run on Windows). + * Fix pickling blueprints Change the string passed in the "name" section of the namedtuples in Blueprint to match the name of the Blueprint module attribute name. This allows blueprints to be pickled and unpickled, without errors, which is a requirement of running Sanic in multiprocessing mode in Windows. Added a test for pickling and unpickling blueprints Added a test for pickling and unpickling sanic itself Added a test for enabling multiprocessing on an app with a blueprint (only useful to catch this bug if the tests are run on Windows). * Fix document for logging Version 0.8 @@ -1129,7 +1129,7 @@ Version 0.8 * Content-length header on 204/304 responses (Arnulfo Solís) * Extend WebSocketProtocol arguments and add docs (Bob Olde Hampsink, yunstanford) * Update development status from pre-alpha to beta (Maksim Anisenkov) - * KeepAlive Timout log level changed to debug (Arnulfo Solís) + * KeepAlive Timeout log level changed to debug (Arnulfo Solís) * Pin pytest to 3.3.2 because of pytest-dev/pytest#3170 (Maksim Aniskenov) * Install Python 3.5 and 3.6 on docker container for tests (Shahin Azad) * Add support for blueprint groups and nesting (Elias Tarhini) diff --git a/examples/amending_request_object.py b/examples/amending_request_object.py index 7fe25bdd..366dd67d 100644 --- a/examples/amending_request_object.py +++ b/examples/amending_request_object.py @@ -23,7 +23,7 @@ def key_exist_handler(request): if hasattr(request.ctx, "num"): return text("num exist in request") - return text("num does not exist in reqeust") + return text("num does not exist in request") app.run(host="0.0.0.0", port=8000, debug=True) diff --git a/sanic/app.py b/sanic/app.py index 6861bca0..5c0a010e 100644 --- a/sanic/app.py +++ b/sanic/app.py @@ -337,7 +337,7 @@ class Sanic(BaseSanic, metaclass=TouchUpMeta): """ Method for attaching middleware to specific routes. This is mainly an internal tool for use by Blueprints to attach middleware to only its - specfic routes. But, it could be used in a more generalized fashion. + specific routes. But, it could be used in a more generalized fashion. :param middleware: the middleware to execute :param route_names: a list of the names of the endpoints diff --git a/sanic/blueprints.py b/sanic/blueprints.py index 290773fa..6a6c2e82 100644 --- a/sanic/blueprints.py +++ b/sanic/blueprints.py @@ -79,7 +79,7 @@ class Blueprint(BaseSanic): :param name: unique name of the blueprint :param url_prefix: URL to be prefixed before all route URLs - :param host: IP Address of FQDN for the sanic server to use. + :param host: IP Address or FQDN for the sanic server to use. :param version: Blueprint Version :param strict_slashes: Enforce the API urls are requested with a trailing */* @@ -112,7 +112,7 @@ class Blueprint(BaseSanic): self, name: str = None, url_prefix: Optional[str] = None, - host: Optional[str] = None, + host: Optional[Union[List[str], str]] = None, version: Optional[Union[int, str, float]] = None, strict_slashes: Optional[bool] = None, version_prefix: str = "/v", diff --git a/sanic/headers.py b/sanic/headers.py index dbb8720f..b744974c 100644 --- a/sanic/headers.py +++ b/sanic/headers.py @@ -28,7 +28,7 @@ _host_re = re.compile( # RFC's quoted-pair escapes are mostly ignored by browsers. Chrome, Firefox and # curl all have different escaping, that we try to handle as well as possible, -# even though no client espaces in a way that would allow perfect handling. +# even though no client escapes in a way that would allow perfect handling. # For more information, consult ../tests/test_requests.py diff --git a/sanic/helpers.py b/sanic/helpers.py index 87d51b53..c5c10ccc 100644 --- a/sanic/helpers.py +++ b/sanic/helpers.py @@ -144,7 +144,7 @@ def import_string(module_name, package=None): import a module or class by string path. :module_name: str with path of module or path to import and - instanciate a class + instantiate a class :returns: a module object or one instance from class if module_name is a valid path to class diff --git a/sanic/router.py b/sanic/router.py index b15c2a3e..bad471c6 100644 --- a/sanic/router.py +++ b/sanic/router.py @@ -54,7 +54,7 @@ class Router(BaseRouter): self, path: str, method: str, host: Optional[str] ) -> Tuple[Route, RouteHandler, Dict[str, Any]]: """ - Retrieve a `Route` object containg the details about how to handle + Retrieve a `Route` object containing the details about how to handle a response for a given request :param request: the incoming request object diff --git a/sanic/tls.py b/sanic/tls.py index e0f9151a..be30f4a2 100644 --- a/sanic/tls.py +++ b/sanic/tls.py @@ -175,7 +175,7 @@ def match_hostname( def selector_sni_callback( sslobj: ssl.SSLObject, server_name: str, ctx: CertSelector ) -> Optional[int]: - """Select a certificate mathing the SNI.""" + """Select a certificate matching the SNI.""" # Call server_name_callback to store the SNI on sslobj server_name_callback(sslobj, server_name, ctx) # Find a new context matching the hostname diff --git a/sanic/utils.py b/sanic/utils.py index ef91ec9d..51d94d08 100644 --- a/sanic/utils.py +++ b/sanic/utils.py @@ -48,7 +48,7 @@ def load_module_from_file_location( """Returns loaded module provided as a file path. :param args: - Coresponds to importlib.util.spec_from_file_location location + Corresponds to importlib.util.spec_from_file_location location parameters,but with this differences: - It has to be of a string or bytes type. - You can also use here environment variables @@ -58,10 +58,10 @@ def load_module_from_file_location( If location parameter is of a bytes type, then use this encoding to decode it into string. :param args: - Coresponds to the rest of importlib.util.spec_from_file_location + Corresponds to the rest of importlib.util.spec_from_file_location parameters. :param kwargs: - Coresponds to the rest of importlib.util.spec_from_file_location + Corresponds to the rest of importlib.util.spec_from_file_location parameters. For example You can: diff --git a/scripts/release.py b/scripts/release.py index 488ebe2b..e2b9b887 100755 --- a/scripts/release.py +++ b/scripts/release.py @@ -310,7 +310,7 @@ if __name__ == "__main__": cli.add_argument( "--milestone", "-ms", - help="Git Release milestone information to include in relase note", + help="Git Release milestone information to include in release note", required=False, ) cli.add_argument( diff --git a/setup.py b/setup.py index 3bc11f8e..36de0c4f 100644 --- a/setup.py +++ b/setup.py @@ -121,6 +121,7 @@ docs_require = [ "docutils", "pygments", "m2r2", + "mistune<2.0.0", ] dev_require = tests_require + [ diff --git a/tests/test_request_data.py b/tests/test_request_data.py index f5bfabda..a1b78e95 100644 --- a/tests/test_request_data.py +++ b/tests/test_request_data.py @@ -17,7 +17,7 @@ def test_custom_context(app): @app.route("/") def handler(request): - # Accessing non-existant key should fail with AttributeError + # Accessing non-existent key should fail with AttributeError try: invalid = request.ctx.missing except AttributeError as e: