Repository: litestar-org/litestar

Issue:
Bug: Cannot generate OpenApi schema for Literal of Literals
### Description

`Literal` may be parameterized by other literal types (see https://peps.python.org/pep-0586/#legal-parameters-for-literal-at-type-check-time) but currently such annotations break the OpenApi generation.

### MCVE
Modified version of [test_handling_of_literals](https://github.com/litestar-org/litestar/blob/main/tests/openapi/test_schema.py#L127):

```python
def test_composite_literals() -> None:
    Letters = Literal["a", "b", "c"]
    Digits = Literal[1, 2, 3]

    @dataclass
    class DataclassWithLiteral:
        composite: Literal[Letters, Digits]

    schemas: Dict[str, Schema] = {}
    result = create_schema(
        field=SignatureField.create(name="", field_type=DataclassWithLiteral),
        generate_examples=False,
        plugins=[],
        schemas=schemas,
        prefer_alias=True,
    )
```

Output: `KeyError: <class 'typing._GenericAlias'>`

### Litestar Version

Main branch



Discussion:

Resolving patch:
diff --git a/litestar/_openapi/schema_generation/schema.py b/litestar/_openapi/schema_generation/schema.py
--- a/litestar/_openapi/schema_generation/schema.py
+++ b/litestar/_openapi/schema_generation/schema.py
@@ -17,6 +17,7 @@
     Hashable,
     Iterable,
     List,
+    Literal,
     Mapping,
     MutableMapping,
     MutableSequence,
@@ -301,6 +302,22 @@ def create_enum_schema(annotation: EnumMeta) -> Schema:
     return Schema(type=openapi_type, enum=enum_values)
 
 
+def iter_flat_literal_args(annotation: Any) -> Iterable[Any]:
+    """Iterate over the flattened arguments of a Literal.
+
+    Args:
+        annotation: An Literal annotation.
+
+    Yields:
+        The flattened arguments of the Literal.
+    """
+    for arg in get_args(annotation):
+        if get_origin_or_inner_type(arg) is Literal:
+            yield from iter_flat_literal_args(arg)
+        else:
+            yield arg
+
+
 def create_literal_schema(annotation: Any) -> Schema:
     """Create a schema instance for a Literal.
 
@@ -310,7 +327,7 @@ def create_literal_schema(annotation: Any) -> Schema:
     Returns:
         A schema instance.
     """
-    args = get_args(annotation)
+    args = tuple(iter_flat_literal_args(annotation))
     schema = copy(TYPE_MAP[type(args[0])])
     if len(args) > 1:
         schema.enum = args


Test patch:
diff --git a/tests/openapi/test_schema.py b/tests/openapi/test_schema.py
--- a/tests/openapi/test_schema.py
+++ b/tests/openapi/test_schema.py
@@ -125,10 +125,14 @@ class M(BaseModel):
 
 
 def test_handling_of_literals() -> None:
+    ValueType = Literal["a", "b", "c"]
+    ConstType = Literal[1]
+
     @dataclass
     class DataclassWithLiteral:
-        value: Literal["a", "b", "c"]
-        const: Literal[1]
+        value: ValueType
+        const: ConstType
+        composite: Literal[ValueType, ConstType]
 
     schemas: Dict[str, Schema] = {}
     result = create_schema(
@@ -138,16 +142,23 @@ class DataclassWithLiteral:
         schemas=schemas,
     )
     assert isinstance(result, Reference)
+
     schema = schemas["DataclassWithLiteral"]
     assert isinstance(schema, Schema)
     assert schema.properties
+
     value = schema.properties["value"]
     assert isinstance(value, Schema)
     assert value.enum == ("a", "b", "c")
+
     const = schema.properties["const"]
     assert isinstance(const, Schema)
     assert const.const == 1
 
+    composite = schema.properties["composite"]
+    assert isinstance(composite, Schema)
+    assert composite.enum == ("a", "b", "c", 1)
+
 
 def test_schema_hashing() -> None:
     schema = Schema(