Closed nicolaipre closed 6 months ago
FWIW; I just ran a new test after rebooting and all the initial tests seems to pass now with the following change:
$ git diff
diff --git a/sanic/blueprints.py b/sanic/blueprints.py
index 6617d09c..5b9f0f2e 100644
--- a/sanic/blueprints.py
+++ b/sanic/blueprints.py
@@ -59,7 +59,7 @@ def lazy(func, as_decorator=True):
kwargs["apply"] = False
pass_handler = None
- if args and isfunction(args[0]):
+ if args and callable(args[0]):
as_decorator = False
def wrapper(handler):
Test output:
$ tox
py38: skipped because could not find python interpreter with spec(s): py38
py38: SKIP ⚠ in 0 seconds
py39: skipped because could not find python interpreter with spec(s): py39
py39: SKIP ⚠ in 0 seconds
.pkg: _optional_hooks> python /home/user/test/sanic/venv/lib/python3.10/site-packages/pyproject_api/_backend.py True setuptools.build_meta
.pkg: get_requires_for_build_editable> python /home/user/test/sanic/venv/lib/python3.10/site-packages/pyproject_api/_backend.py True setuptools.build_meta
.pkg: build_editable> python /home/user/test/sanic/venv/lib/python3.10/site-packages/pyproject_api/_backend.py True setuptools.build_meta
py310: install_package> python -I -m pip install --force-reinstall --no-deps /home/user/test/sanic/.tox/.tmp/package/5/sanic-23.12.0-0.editable-py3-none-any.whl
py310: commands[0]> coverage run --source ./sanic -m pytest tests
========================================================================================================== test session starts ==========================================================================================================
platform linux -- Python 3.10.12, pytest-7.1.3, pluggy-1.4.0
cachedir: .tox/py310/.pytest_cache
benchmark: 4.0.0 (defaults: timer=time.perf_counter disable_gc=False min_rounds=5 min_time=0.000005 max_time=1.0 calibration_precision=10 warmup=False warmup_iterations=100000)
rootdir: /home/user/test/sanic, configfile: tox.ini
plugins: sanic-1.9.1, anyio-4.2.0, benchmark-4.0.0
collected 1685 items
tests/test_app.py ........................................................ [ 3%]
tests/test_asgi.py ........................ [ 4%]
tests/test_bad_request.py . [ 4%]
tests/test_base.py ..................... [ 6%]
tests/test_blueprint_copy.py ... [ 6%]
tests/test_blueprint_group.py ........... [ 6%]
tests/test_blueprints.py .............................................. [ 9%]
tests/test_cancellederror.py . [ 9%]
tests/test_cli.py ...................................................... [ 12%]
tests/test_coffee.py .... [ 13%]
tests/test_config.py ......................................... [ 15%]
tests/test_constants.py ...... [ 15%]
tests/test_cookies.py ................................... [ 17%]
tests/test_create_task.py ..... [ 18%]
tests/test_custom_request.py . [ 18%]
tests/test_deprecation.py .... [ 18%]
tests/test_dynamic_routes.py .... [ 18%]
tests/test_errorpages.py ...................................................... [ 22%]
tests/test_exceptions.py ..................... [ 23%]
tests/test_exceptions_handler.py ............. [ 24%]
tests/test_ext_integration.py ....... [ 24%]
tests/test_graceful_shutdown.py X. [ 24%]
tests/test_handler.py . [ 24%]
tests/test_handler_annotations.py .... [ 24%]
tests/test_headers.py ........................................................................ [ 29%]
tests/test_helpers.py ....... [ 29%]
tests/test_http.py ... [ 29%]
tests/test_http_alt_svc.py . [ 29%]
tests/test_init.py ............. [ 30%]
tests/test_json_decoding.py ... [ 30%]
tests/test_json_encoding.py ...s [ 30%]
tests/test_keep_alive_timeout.py .... [ 31%]
tests/test_late_adds.py ... [ 31%]
tests/test_logging.py ................... [ 32%]
tests/test_logo.py ..... [ 32%]
tests/test_middleware.py ............... [ 33%]
tests/test_middleware_priority.py .................................................... [ 36%]
tests/test_motd.py ..... [ 37%]
tests/test_multiprocessing.py ......... [ 37%]
tests/test_named_routes.py .......................... [ 39%]
tests/test_payload_too_large.py ... [ 39%]
tests/test_pipelining.py .... [ 39%]
tests/test_prepare.py ..... [ 39%]
tests/test_redirect.py ......... [ 40%]
tests/test_reloader.py XxXxSXX. [ 40%]
tests/test_request.py .......................................... [ 43%]
tests/test_request_cancel.py .. [ 43%]
tests/test_request_data.py .. [ 43%]
tests/test_request_stream.py ......... [ 44%]
tests/test_requests.py .................................................................................................................................... [ 51%]
tests/test_response.py ........................................................... [ 55%]
tests/test_response_file.py ...... [ 55%]
tests/test_response_json.py ................ [ 56%]
tests/test_response_timeout.py .... [ 56%]
tests/test_routes.py ................................................................................................................................................................. [ 66%]
tests/test_server_events.py .................... [ 67%]
tests/test_server_loop.py ss.. [ 67%]
tests/test_signal_handlers.py ...... [ 68%]
tests/test_signals.py ................................................. [ 71%]
tests/test_static.py ..........sssss...........................................................................s [ 76%]
tests/test_static_directory.py ....... [ 77%]
tests/test_tasks.py ...... [ 77%]
tests/test_test_client_port.py .. [ 77%]
tests/test_timeout_logic.py ..... [ 77%]
tests/test_tls.py ........xxx................................... [ 80%]
tests/test_touchup.py ......... [ 81%]
tests/test_unix_socket.py X........ [ 81%]
tests/test_url_building.py ........................... [ 83%]
tests/test_url_for.py ......... [ 83%]
tests/test_url_for_static.py ..................... [ 84%]
tests/test_utf8.py ... [ 85%]
tests/test_utils.py ...... [ 85%]
tests/test_versioning.py .............. [ 86%]
tests/test_vhosts.py ... [ 86%]
tests/test_views.py ................... [ 87%]
tests/test_websockets.py ............................... [ 89%]
tests/test_ws_handlers.py ........ [ 89%]
tests/benchmark/test_route_resolution_benchmark.py .. [ 90%]
tests/http3/test_http_receiver.py ............ [ 90%]
tests/http3/test_server.py .... [ 91%]
tests/http3/test_session_ticket_store.py . [ 91%]
tests/typing/test_typing.py ....... [ 91%]
tests/worker/test_inspector.py ......... [ 92%]
tests/worker/test_loader.py ............ [ 92%]
tests/worker/test_manager.py ........................ [ 94%]
tests/worker/test_multiplexer.py .................. [ 95%]
tests/worker/test_reloader.py .......... [ 95%]
tests/worker/test_restarter.py ........... [ 96%]
tests/worker/test_runner.py .... [ 96%]
tests/worker/test_shared_ctx.py ............... [ 97%]
tests/worker/test_socket.py .. [ 97%]
tests/worker/test_startup.py ......... [ 98%]
tests/worker/test_state.py .................... [ 99%]
tests/worker/test_worker_serve.py ......... [100%]
=========================================================================================================== warnings summary ============================================================================================================
tests/test_asgi.py: 1 warning
tests/test_cookies.py: 18 warnings
tests/test_response.py: 1 warning
...
Cleaned-up code example to test with - middleware registration now works as expected:
#!/usr/bin/env python3
from sanic import Sanic, Blueprint
from sanic.request import Request
from sanic.response import text, json
class MiddlewareAsClass:
name = "test"
attach_to = "request"
apply = True # does this even work? Doesnt seem to reflect whether True/False
priority = 0
def __call__(self, request: Request):
print("MiddlewareAsClass called")
return text("Middleware check failed!\n")
bp = Blueprint("my_blueprint")
@bp.route("/")
async def bp_root(request: Request):
return text("Home\n")
mw = MiddlewareAsClass()
# works fine
bp.middleware(mw, attach_to=mw.attach_to, apply=mw.apply, priority=mw.priority)
# doesnt work if you set "middleware_or_request" ?????
#bp.middleware(middleware_or_request=mw, attach_to=mw.attach_to, apply=mw.apply, priority=mw.priority)
app = Sanic("MiddlewareTestApp")
app.blueprint(bp)
if __name__ == "__main__":
app.run(host="127.0.0.1", port=8000, dev=True)
$ curl 127.0.0.1:8000
Middleware check failed!
Closed #2923 as it was solved with a wrapper function instead.
Is there an existing issue for this?
Describe the bug
I was playing around with middleware registration directly on a blueprint, since I want to register it directly without decorators. I therefore used bp.middleware(middleware) and noticed something quite odd in the process where middleware does not seem to get added if you are using a callable class with
__call__()
Code snippet
Expected Behavior
I noticed that middleware did not work if I used a callable class, unless I registered a middleware that was a function first, i.e:
My friends and I did some debugging on this as we couldn't figure out why, and it turns out that it is related to the lazy() function from this commit. The interesting code region is between lines 47 and 63 in
sanic/blueprints.py
, specifically the call toisfunction(args[0])
which will return False if you use a callable class such asMiddlewareAsClass
in the code example above (unless you already have registered a middleware function before that). If you callMiddlewareAsClass.__call__
directly it works works becauseisfunction(args[0])
then returnsTrue
.Attempted fixes:
By defaulting
as_decorator=False
the class middleware gets registered and seems to work fine (from some manual testing) for both decorator and non-decorator registration. After running the test-suite with this change the following test to fail:tests/test_blueprints.py:1084: AssertionError
.Replacing
isfunction(args[0])
withcallable(args[0])
. Using this "fix" the testtest_blueprints.py
passes fine, but causes a few other tests to fail. Alternatively this:I am not sure what the best possible solution would be here, so I created the issue as requested in Discord #support.
How do you run Sanic?
As a script (
app.run
orSanic.serve
)Operating System
Linux
Sanic Version
23.12.1
Additional context
No response