Repository: litestar-org/litestar

Issue:
StaticFilesConfig and virtual directories
I'm trying to write a ``FileSystemProtocol`` to load files from the package data using [importlib_resources](https://importlib-resources.readthedocs.io/en/latest/using.html#). But because ``directories`` is defined as ``DirectoryPath``, pydantic checks if the given directories exist in the local filesystem. 

This is not generally true, especially in any kind of virtual filesystem (e.g. a zipped package). I think this condition should be relaxed to support virtual filesystems.

https://github.com/starlite-api/starlite/blob/9bb6dcd57c10a591377cf8e3a537e9292566d5b9/starlite/config/static_files.py#L32


Discussion:
I agree. Do you want to submit a PR?

Resolving patch:
diff --git a/litestar/middleware/exceptions/middleware.py b/litestar/middleware/exceptions/middleware.py
--- a/litestar/middleware/exceptions/middleware.py
+++ b/litestar/middleware/exceptions/middleware.py
@@ -109,9 +109,15 @@ def create_exception_response(exc: Exception) -> Response:
     Returns:
         Response: HTTP response constructed from exception details.
     """
+    status_code = getattr(exc, "status_code", HTTP_500_INTERNAL_SERVER_ERROR)
+    if status_code == HTTP_500_INTERNAL_SERVER_ERROR:
+        detail = "Internal Server Error"
+    else:
+        detail = getattr(exc, "detail", repr(exc))
+
     content = ExceptionResponseContent(
-        status_code=getattr(exc, "status_code", HTTP_500_INTERNAL_SERVER_ERROR),
-        detail=getattr(exc, "detail", repr(exc)),
+        status_code=status_code,
+        detail=detail,
         headers=getattr(exc, "headers", None),
         extra=getattr(exc, "extra", None),
     )


Test patch:
diff --git a/tests/examples/test_dto/test_example_apps.py b/tests/examples/test_dto/test_example_apps.py
--- a/tests/examples/test_dto/test_example_apps.py
+++ b/tests/examples/test_dto/test_example_apps.py
@@ -11,7 +11,6 @@ def test_dto_data_problem_statement_app() -> None:
     with TestClient(app) as client:
         response = client.post("/person", json={"name": "John", "age": 30})
         assert response.status_code == 500
-        assert "missing 1 required positional argument: 'id'" in response.json()["detail"]
 
 
 def test_dto_data_usage_app() -> None:
diff --git a/tests/examples/test_dto/test_tutorial.py b/tests/examples/test_dto/test_tutorial.py
--- a/tests/examples/test_dto/test_tutorial.py
+++ b/tests/examples/test_dto/test_tutorial.py
@@ -102,7 +102,6 @@ def test_read_only_fields():
         response = client.post("/person", json={"name": "peter", "age": 40, "email": "email_of_peter@example.com"})
 
     assert response.status_code == 500
-    assert "__init__() missing 1 required positional argument: 'id'" in response.json()["detail"]
 
 
 def test_dto_data():
diff --git a/tests/unit/test_exceptions.py b/tests/unit/test_exceptions.py
--- a/tests/unit/test_exceptions.py
+++ b/tests/unit/test_exceptions.py
@@ -87,7 +87,7 @@ def test_create_exception_response_utility_non_http_exception() -> None:
     response = create_exception_response(exc)
     assert response.status_code == HTTP_500_INTERNAL_SERVER_ERROR
     assert response.media_type == MediaType.JSON
-    assert response.content == {"status_code": 500, "detail": "RuntimeError('yikes')"}
+    assert response.content == {"status_code": 500, "detail": "Internal Server Error"}
 
 
 def test_missing_dependency_exception() -> None:
diff --git a/tests/unit/test_kwargs/test_generator_dependencies.py b/tests/unit/test_kwargs/test_generator_dependencies.py
--- a/tests/unit/test_kwargs/test_generator_dependencies.py
+++ b/tests/unit/test_kwargs/test_generator_dependencies.py
@@ -120,7 +120,7 @@ def handler(dep: str) -> Dict[str, str]:
     with create_test_client(route_handlers=[handler]) as client:
         res = client.get("/")
         assert res.status_code == 500
-        assert res.json() == {"detail": "ValueError('foo')", "status_code": 500}
+        assert res.json() == {"detail": "Internal Server Error", "status_code": 500}
         cleanup_mock.assert_not_called()
         exception_mock.assert_called_once()
         finally_mock.assert_called_once()
@@ -144,7 +144,7 @@ def handler(dep: str) -> Dict[str, str]:
     with create_test_client(route_handlers=[handler]) as client:
         res = client.get("/")
         assert res.status_code == 500
-        assert res.json() == {"status_code": 500, "detail": "Exception('foo')"}
+        assert res.json() == {"status_code": 500, "detail": "Internal Server Error"}
         cleanup_mock.assert_called_once()
         finally_mock.assert_called_once()
 
diff --git a/tests/unit/test_middleware/test_base_authentication_middleware.py b/tests/unit/test_middleware/test_base_authentication_middleware.py
--- a/tests/unit/test_middleware/test_base_authentication_middleware.py
+++ b/tests/unit/test_middleware/test_base_authentication_middleware.py
@@ -72,7 +72,6 @@ def http_route_handler_user_scope(request: Request[User, None, Any]) -> None:
     client = create_test_client(route_handlers=[http_route_handler_user_scope])
     error_response = client.get("/", headers={"Authorization": "nope"})
     assert error_response.status_code == HTTP_500_INTERNAL_SERVER_ERROR
-    assert error_response.json()["detail"] == "'user' is not defined in scope, install an AuthMiddleware to set it"
 
 
 def test_authentication_middleware_not_installed_raises_for_auth_scope_http() -> None:
@@ -83,7 +82,6 @@ def http_route_handler_auth_scope(request: Request[None, Auth, Any]) -> None:
     client = create_test_client(route_handlers=[http_route_handler_auth_scope])
     error_response = client.get("/", headers={"Authorization": "nope"})
     assert error_response.status_code == HTTP_500_INTERNAL_SERVER_ERROR
-    assert error_response.json()["detail"] == "'auth' is not defined in scope, install an AuthMiddleware to set it"
 
 
 @websocket(path="/")
diff --git a/tests/unit/test_middleware/test_exception_handler_middleware.py b/tests/unit/test_middleware/test_exception_handler_middleware.py
--- a/tests/unit/test_middleware/test_exception_handler_middleware.py
+++ b/tests/unit/test_middleware/test_exception_handler_middleware.py
@@ -40,7 +40,7 @@ def test_default_handle_http_exception_handling_extra_object() -> None:
     )
     assert response.status_code == HTTP_500_INTERNAL_SERVER_ERROR
     assert response.content == {
-        "detail": "litestar_exception",
+        "detail": "Internal Server Error",
         "extra": {"key": "value"},
         "status_code": 500,
     }
@@ -52,7 +52,7 @@ def test_default_handle_http_exception_handling_extra_none() -> None:
         HTTPException(detail="litestar_exception"),
     )
     assert response.status_code == HTTP_500_INTERNAL_SERVER_ERROR
-    assert response.content == {"detail": "litestar_exception", "status_code": 500}
+    assert response.content == {"detail": "Internal Server Error", "status_code": 500}
 
 
 def test_default_handle_litestar_http_exception_handling() -> None:
@@ -61,7 +61,7 @@ def test_default_handle_litestar_http_exception_handling() -> None:
         HTTPException(detail="litestar_exception"),
     )
     assert response.status_code == HTTP_500_INTERNAL_SERVER_ERROR
-    assert response.content == {"detail": "litestar_exception", "status_code": 500}
+    assert response.content == {"detail": "Internal Server Error", "status_code": 500}
 
 
 def test_default_handle_litestar_http_exception_extra_list() -> None:
@@ -71,7 +71,7 @@ def test_default_handle_litestar_http_exception_extra_list() -> None:
     )
     assert response.status_code == HTTP_500_INTERNAL_SERVER_ERROR
     assert response.content == {
-        "detail": "litestar_exception",
+        "detail": "Internal Server Error",
         "extra": ["extra-1", "extra-2"],
         "status_code": 500,
     }
@@ -84,7 +84,7 @@ def test_default_handle_starlette_http_exception_handling() -> None:
     )
     assert response.status_code == HTTP_500_INTERNAL_SERVER_ERROR
     assert response.content == {
-        "detail": "litestar_exception",
+        "detail": "Internal Server Error",
         "status_code": 500,
     }
 
@@ -95,7 +95,7 @@ def test_default_handle_python_http_exception_handling() -> None:
     )
     assert response.status_code == HTTP_500_INTERNAL_SERVER_ERROR
     assert response.content == {
-        "detail": repr(AttributeError("oops")),
+        "detail": "Internal Server Error",
         "status_code": HTTP_500_INTERNAL_SERVER_ERROR,
     }
 
@@ -164,7 +164,10 @@ def handler() -> None:
         client.app.logger = get_logger("litestar")
         response = client.get("/test")
         assert response.status_code == HTTP_500_INTERNAL_SERVER_ERROR
-        assert "Test debug exception" in response.text
+        if is_debug:
+            assert "Test debug exception" in response.text
+        else:
+            assert "Internal Server Error" in response.text
 
         if should_log:
             assert len(caplog.records) == 1
@@ -206,7 +209,10 @@ def handler() -> None:
     with TestClient(app=app) as client, capture_logs() as cap_logs:
         response = client.get("/test")
         assert response.status_code == HTTP_500_INTERNAL_SERVER_ERROR
-        assert "Test debug exception" in response.text
+        if is_debug:
+            assert "Test debug exception" in response.text
+        else:
+            assert "Internal Server Error" in response.text
 
         if should_log:
             assert len(cap_logs) == 1
@@ -233,7 +239,7 @@ def handler() -> None:
         client.app.logger = get_logger("litestar")
         response = client.get("/test")
         assert response.status_code == HTTP_500_INTERNAL_SERVER_ERROR
-        assert "Test debug exception" in response.text
+        assert "Internal Server Error" in response.text
 
         assert len(caplog.records) == 1
         assert caplog.records[0].levelname == "ERROR"
diff --git a/tests/unit/test_middleware/test_session/test_middleware.py b/tests/unit/test_middleware/test_session/test_middleware.py
--- a/tests/unit/test_middleware/test_session/test_middleware.py
+++ b/tests/unit/test_middleware/test_session/test_middleware.py
@@ -19,7 +19,7 @@ def handler(request: Request) -> None:
     with create_test_client(handler) as client:
         response = client.get("/test")
         assert response.status_code == HTTP_500_INTERNAL_SERVER_ERROR
-        assert response.json()["detail"] == "'session' is not defined in scope, install a SessionMiddleware to set it"
+        assert response.json()["detail"] == "Internal Server Error"
 
 
 def test_integration(session_backend_config: "BaseBackendConfig") -> None:
diff --git a/tests/unit/test_signature/test_parsing.py b/tests/unit/test_signature/test_parsing.py
--- a/tests/unit/test_signature/test_parsing.py
+++ b/tests/unit/test_signature/test_parsing.py
@@ -146,7 +146,7 @@ def test(dep: int, param: int, optional_dep: Optional[int] = Dependency()) -> No
         response = client.get("/?param=13")
 
     assert response.json() == {
-        "detail": "A dependency failed validation for GET http://testserver.local/?param=13",
+        "detail": "Internal Server Error",
         "extra": error_extra,
         "status_code": HTTP_500_INTERNAL_SERVER_ERROR,
     }
diff --git a/tests/unit/test_template/test_built_in.py b/tests/unit/test_template/test_built_in.py
--- a/tests/unit/test_template/test_built_in.py
+++ b/tests/unit/test_template/test_built_in.py
@@ -93,7 +93,7 @@ def invalid_template_name_handler() -> Template:
     with create_test_client(route_handlers=[invalid_template_name_handler], template_config=template_config) as client:
         response = client.request("GET", "/")
         assert response.status_code == 500
-        assert response.json() == {"detail": "Template invalid.html not found.", "status_code": 500}
+        assert response.json() == {"detail": "Internal Server Error", "status_code": 500}
 
 
 def test_no_context(tmp_path: Path, template_config: TemplateConfig) -> None:
diff --git a/tests/unit/test_template/test_template.py b/tests/unit/test_template/test_template.py
--- a/tests/unit/test_template/test_template.py
+++ b/tests/unit/test_template/test_template.py
@@ -25,7 +25,7 @@ def invalid_path() -> Template:
     with create_test_client(route_handlers=[invalid_path]) as client:
         response = client.request("GET", "/")
         assert response.status_code == 500
-        assert response.json() == {"detail": "Template engine is not configured", "status_code": 500}
+        assert response.json() == {"detail": "Internal Server Error", "status_code": 500}
 
 
 def test_engine_passed_to_callback(tmp_path: "Path") -> None: