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/dto/_backend.py b/litestar/dto/_backend.py
--- a/litestar/dto/_backend.py
+++ b/litestar/dto/_backend.py
@@ -46,11 +46,11 @@ class DTOBackend:
         "handler_id",
         "is_data_field",
         "model_type",
+        "override_serialization_name",
         "parsed_field_definitions",
         "reverse_name_map",
         "transfer_model_type",
         "wrapper_attribute_name",
-        "is_dto_data_type",
     )
 
     _seen_model_names: ClassVar[set[str]] = set()
@@ -88,12 +88,11 @@ def __init__(
             model_name=model_type.__name__, field_definitions=self.parsed_field_definitions
         )
         self.dto_data_type: type[DTOData] | None = None
-        self.is_dto_data_type: bool = False
 
+        self.override_serialization_name: bool = False
         if field_definition.is_subclass_of(DTOData):
             self.dto_data_type = field_definition.annotation
             annotation = self.field_definition.inner_types[0].annotation
-            self.is_dto_data_type = True
         else:
             annotation = field_definition.annotation
 
@@ -245,28 +244,33 @@ def populate_data_from_builtins(self, builtins: Any, asgi_connection: ASGIConnec
                     field_definitions=self.parsed_field_definitions,
                     field_definition=self.field_definition,
                     is_data_field=self.is_data_field,
-                    is_dto_data_type=self.is_dto_data_type,
+                    override_serialization_name=self.override_serialization_name,
                 ),
             )
         return self.transfer_data_from_builtins(self.parse_builtins(builtins, asgi_connection))
 
-    def transfer_data_from_builtins(self, builtins: Any) -> Any:
+    def transfer_data_from_builtins(self, builtins: Any, override_serialization_name: bool = False) -> Any:
         """Populate model instance from builtin types.
 
         Args:
             builtins: Builtin type.
+            override_serialization_name: Use the original field names, used when creating
+                                         an instance using `DTOData.create_instance`
 
         Returns:
             Instance or collection of ``model_type`` instances.
         """
-        return _transfer_data(
+        self.override_serialization_name = override_serialization_name
+        data = _transfer_data(
             destination_type=self.model_type,
             source_data=builtins,
             field_definitions=self.parsed_field_definitions,
             field_definition=self.field_definition,
             is_data_field=self.is_data_field,
-            is_dto_data_type=self.is_dto_data_type,
+            override_serialization_name=self.override_serialization_name,
         )
+        self.override_serialization_name = False
+        return data
 
     def populate_data_from_raw(self, raw: bytes, asgi_connection: ASGIConnection) -> Any:
         """Parse raw bytes into instance of `model_type`.
@@ -287,7 +291,7 @@ def populate_data_from_raw(self, raw: bytes, asgi_connection: ASGIConnection) ->
                     field_definitions=self.parsed_field_definitions,
                     field_definition=self.field_definition,
                     is_data_field=self.is_data_field,
-                    is_dto_data_type=self.is_dto_data_type,
+                    override_serialization_name=self.override_serialization_name,
                 ),
             )
         return _transfer_data(
@@ -296,7 +300,7 @@ def populate_data_from_raw(self, raw: bytes, asgi_connection: ASGIConnection) ->
             field_definitions=self.parsed_field_definitions,
             field_definition=self.field_definition,
             is_data_field=self.is_data_field,
-            is_dto_data_type=self.is_dto_data_type,
+            override_serialization_name=self.override_serialization_name,
         )
 
     def encode_data(self, data: Any) -> LitestarEncodableType:
@@ -315,7 +319,7 @@ def encode_data(self, data: Any) -> LitestarEncodableType:
                 field_definitions=self.parsed_field_definitions,
                 field_definition=self.field_definition,
                 is_data_field=self.is_data_field,
-                is_dto_data_type=self.is_dto_data_type,
+                override_serialization_name=self.override_serialization_name,
             )
             setattr(
                 data,
@@ -332,7 +336,7 @@ def encode_data(self, data: Any) -> LitestarEncodableType:
                 field_definitions=self.parsed_field_definitions,
                 field_definition=self.field_definition,
                 is_data_field=self.is_data_field,
-                is_dto_data_type=self.is_dto_data_type,
+                override_serialization_name=self.override_serialization_name,
             ),
         )
 
@@ -521,7 +525,7 @@ def _transfer_data(
     field_definitions: tuple[TransferDTOFieldDefinition, ...],
     field_definition: FieldDefinition,
     is_data_field: bool,
-    is_dto_data_type: bool,
+    override_serialization_name: bool,
 ) -> Any:
     """Create instance or iterable of instances of ``destination_type``.
 
@@ -531,7 +535,8 @@ def _transfer_data(
         field_definitions: model field definitions.
         field_definition: the parsed type that represents the handler annotation for which the DTO is being applied.
         is_data_field: whether the DTO is being applied to a ``data`` field.
-        is_dto_data_type: whether the field definition is an `DTOData` type.
+        override_serialization_name: Use the original field names, used when creating
+                                     an instance using `DTOData.create_instance`
 
     Returns:
         Data parsed into ``destination_type``.
@@ -544,7 +549,7 @@ def _transfer_data(
                 field_definitions=field_definitions,
                 field_definition=field_definition.inner_types[0],
                 is_data_field=is_data_field,
-                is_dto_data_type=is_dto_data_type,
+                override_serialization_name=override_serialization_name,
             )
             for item in source_data
         )
@@ -554,7 +559,7 @@ def _transfer_data(
         source_instance=source_data,
         field_definitions=field_definitions,
         is_data_field=is_data_field,
-        is_dto_data_type=is_dto_data_type,
+        override_serialization_name=override_serialization_name,
     )
 
 
@@ -563,7 +568,7 @@ def _transfer_instance_data(
     source_instance: Any,
     field_definitions: tuple[TransferDTOFieldDefinition, ...],
     is_data_field: bool,
-    is_dto_data_type: bool,
+    override_serialization_name: bool,
 ) -> Any:
     """Create instance of ``destination_type`` with data from ``source_instance``.
 
@@ -572,7 +577,8 @@ def _transfer_instance_data(
         source_instance: primitive data that has been parsed and validated via the backend.
         field_definitions: model field definitions.
         is_data_field: whether the given field is a 'data' kwarg field.
-        is_dto_data_type: whether the field definition is an `DTOData` type.
+        override_serialization_name: Use the original field names, used when creating
+                                     an instance using `DTOData.create_instance`
 
     Returns:
         Data parsed into ``model_type``.
@@ -580,7 +586,7 @@ def _transfer_instance_data(
     unstructured_data = {}
 
     for field_definition in field_definitions:
-        should_use_serialization_name = is_data_field and not is_dto_data_type
+        should_use_serialization_name = not override_serialization_name and is_data_field
         source_name = field_definition.serialization_name if should_use_serialization_name else field_definition.name
 
         if not is_data_field:
@@ -609,7 +615,7 @@ def _transfer_instance_data(
             transfer_type=transfer_type,
             nested_as_dict=destination_type is dict,
             is_data_field=is_data_field,
-            is_dto_data_type=is_dto_data_type,
+            override_serialization_name=override_serialization_name,
         )
 
     return destination_type(**unstructured_data)
@@ -620,7 +626,7 @@ def _transfer_type_data(
     transfer_type: TransferType,
     nested_as_dict: bool,
     is_data_field: bool,
-    is_dto_data_type: bool,
+    override_serialization_name: bool,
 ) -> Any:
     if isinstance(transfer_type, SimpleType) and transfer_type.nested_field_info:
         if nested_as_dict:
@@ -635,7 +641,7 @@ def _transfer_type_data(
             source_instance=source_value,
             field_definitions=transfer_type.nested_field_info.field_definitions,
             is_data_field=is_data_field,
-            is_dto_data_type=is_dto_data_type,
+            override_serialization_name=override_serialization_name,
         )
 
     if isinstance(transfer_type, UnionType) and transfer_type.has_nested:
@@ -643,7 +649,7 @@ def _transfer_type_data(
             transfer_type=transfer_type,
             source_value=source_value,
             is_data_field=is_data_field,
-            is_dto_data_type=is_dto_data_type,
+            override_serialization_name=override_serialization_name,
         )
 
     if isinstance(transfer_type, CollectionType):
@@ -654,7 +660,7 @@ def _transfer_type_data(
                     transfer_type=transfer_type.inner_type,
                     nested_as_dict=False,
                     is_data_field=is_data_field,
-                    is_dto_data_type=is_dto_data_type,
+                    override_serialization_name=override_serialization_name,
                 )
                 for item in source_value
             )
@@ -667,7 +673,7 @@ def _transfer_nested_union_type_data(
     transfer_type: UnionType,
     source_value: Any,
     is_data_field: bool,
-    is_dto_data_type: bool,
+    override_serialization_name: bool,
 ) -> Any:
     for inner_type in transfer_type.inner_types:
         if isinstance(inner_type, CompositeType):
@@ -684,7 +690,7 @@ def _transfer_nested_union_type_data(
                 source_instance=source_value,
                 field_definitions=inner_type.nested_field_info.field_definitions,
                 is_data_field=is_data_field,
-                is_dto_data_type=is_dto_data_type,
+                override_serialization_name=override_serialization_name,
             )
     return source_value
 
diff --git a/litestar/dto/data_structures.py b/litestar/dto/data_structures.py
--- a/litestar/dto/data_structures.py
+++ b/litestar/dto/data_structures.py
@@ -32,7 +32,9 @@ def create_instance(self, **kwargs: Any) -> T:
         data = dict(self._data_as_builtins)
         for k, v in kwargs.items():
             _set_nested_dict_value(data, k.split("__"), v)
-        return self._backend.transfer_data_from_builtins(data)  # type:ignore[no-any-return]
+        return self._backend.transfer_data_from_builtins(  # type:ignore[no-any-return]
+            data, override_serialization_name=True
+        )
 
     def update_instance(self, instance: T, **kwargs: Any) -> T:
         """Update an instance with the DTO validated data.


Test patch:
diff --git a/tests/unit/test_dto/test_factory/test_backends/test_utils.py b/tests/unit/test_dto/test_factory/test_backends/test_utils.py
--- a/tests/unit/test_dto/test_factory/test_backends/test_utils.py
+++ b/tests/unit/test_dto/test_factory/test_backends/test_utils.py
@@ -57,7 +57,7 @@ def test_transfer_nested_union_type_data_raises_runtime_error_for_complex_union(
             transfer_type=transfer_type,
             is_data_field=True,
             source_value=1,
-            is_dto_data_type=True,
+            override_serialization_name=True,
         )
 
 
diff --git a/tests/unit/test_dto/test_factory/test_integration.py b/tests/unit/test_dto/test_factory/test_integration.py
--- a/tests/unit/test_dto/test_factory/test_integration.py
+++ b/tests/unit/test_dto/test_factory/test_integration.py
@@ -214,12 +214,26 @@ def handler(data: DTOData[User] = Body(media_type=RequestEncodingType.URL_ENCODE
         assert response.json() == {"name": "John", "age": 42, "read_only": "read-only"}
 
 
+RenamedBarT = TypeVar("RenamedBarT")
+
+
 @dataclass
-class RenamedBar:
+class GenericRenamedBar(Generic[RenamedBarT]):
     bar: str
+    spam_bar: RenamedBarT
     foo_foo: str
 
 
+@dataclass
+class InnerBar:
+    best_greeting: str
+
+
+@dataclass
+class RenamedBar(GenericRenamedBar[InnerBar]):
+    pass
+
+
 def test_dto_data_create_instance_renamed_fields() -> None:
     @post(
         dto=DataclassDTO[Annotated[RenamedBar, DTOConfig(exclude={"foo_foo"}, rename_strategy="camel")]],
@@ -229,14 +243,15 @@ def handler(data: DTOData[RenamedBar]) -> RenamedBar:
         assert isinstance(data, DTOData)
         result = data.create_instance(foo_foo="world")
         assert result.foo_foo == "world"
+        assert result.spam_bar.best_greeting == "hello world"
         return result
 
     with create_test_client(
         route_handlers=[handler], signature_namespace={"NestedFoo": NestedFoo, "NestingBar": NestingBar}
     ) as client:
-        response = client.post("/", json={"bar": "hello"})
+        response = client.post("/", json={"bar": "hello", "spamBar": {"bestGreeting": "hello world"}})
         assert response.status_code == 201
-        assert response.json() == {"bar": "hello", "fooFoo": "world"}
+        assert response.json() == {"bar": "hello", "fooFoo": "world", "spamBar": {"bestGreeting": "hello world"}}
 
 
 def test_dto_data_with_patch_request() -> None: