diff --git a/litestar/handlers/http_handlers/base.py b/litestar/handlers/http_handlers/base.py index 1233ac6874..4b6097f9e5 100644 --- a/litestar/handlers/http_handlers/base.py +++ b/litestar/handlers/http_handlers/base.py @@ -53,7 +53,7 @@ ) from litestar.types.builtin_types import NoneType from litestar.utils import ensure_async_callable -from litestar.utils.predicates import is_async_callable +from litestar.utils.predicates import is_async_callable, is_class_and_subclass from litestar.utils.scope.state import ScopeState from litestar.utils.warnings import warn_implicit_sync_to_thread, warn_sync_to_thread_with_async_callable @@ -615,7 +615,15 @@ def _validate_handler_function(self) -> None: if self.http_methods == {HttpMethod.HEAD} and not self.parsed_fn_signature.return_type.is_subclass_of( (NoneType, File, ASGIFileResponse) ): - raise ImproperlyConfiguredException("A response to a head request should not have a body") + field_definition = self.parsed_fn_signature.return_type + if not ( + is_empty_response_annotation(field_definition) + or is_class_and_subclass(field_definition.annotation, File) + or is_class_and_subclass(field_definition.annotation, ASGIFileResponse) + ): + raise ImproperlyConfiguredException( + f"{self}: Handlers for 'HEAD' requests must not return a value. Either return 'None' or a response type without a body." + ) async def handle(self, connection: Request[Any, Any, Any]) -> None: """ASGI app that creates a :class:`~.connection.Request` from the passed in args, determines which handler function to call and then diff --git a/tests/unit/test_handlers/test_http_handlers/test_head.py b/tests/unit/test_handlers/test_http_handlers/test_head.py index 237a11ad83..2808494c93 100644 --- a/tests/unit/test_handlers/test_http_handlers/test_head.py +++ b/tests/unit/test_handlers/test_http_handlers/test_head.py @@ -50,16 +50,6 @@ def handler_subclass() -> MyResponse[None]: Litestar(route_handlers=[handler, handler_subclass]) -def test_head_decorator_raises_validation_error_if_method_is_passed() -> None: - with pytest.raises(ImproperlyConfiguredException): - - @head("/", http_method=HttpMethod.HEAD) - def handler() -> None: - return - - handler.on_registration(Litestar(), HTTPRoute(path="/", route_handlers=[handler])) - - def test_head_decorator_does_not_raise_for_file_response() -> None: @head("/") def handler() -> "File":