Repository: Fatal1ty/mashumaro

Issue:
The types defined inside the function result in syntactically invalid generated code
* mashumaro version: 3.11
* Python version: 3.12
* Operating System: macOS 14.1

### Description

If someone puts dataclasses or other types inside a function, a syntax error will occur. This problem first reported in the following pull requests:
* https://github.com/Fatal1ty/mashumaro/pull/180
* https://github.com/Fatal1ty/mashumaro/pull/181

### What I Did

This code:

```python
def foobar():
    @dataclass
    class Foo:
        foo: int

    @dataclass
    class Bar(DataClassDictMixin):
        foo: Foo

foobar()
```

leads to this exception:

```python
raise MissingField('foo',__main__.foobar.<locals>.Foo,cls) from None
```

There is the `type_name` helper function that may return a string containing `<locals>` if the type is defined in a function scope. This string may then be used inside the generated code. We need to come up with a general solution because `type_name` is used widely across all the code base. One way would be a wrapper for `type_name` helper in `CodeBuilder`:

```python
def type_name(self, typ, *args, **kwargs) -> str:
    name = type_name(typ, *args, **kwargs)
    if "<locals>" in name:
        name = clean_id(name)
        self.ensure_object_imported(typ, name)
    return name
```


Discussion:

Patch:
diff --git a/mashumaro/core/meta/types/unpack.py b/mashumaro/core/meta/types/unpack.py
index 3de5ee8..5900f91 100644
--- a/mashumaro/core/meta/types/unpack.py
+++ b/mashumaro/core/meta/types/unpack.py
@@ -256,7 +256,8 @@ class DiscriminatedUnionUnpackerBuilder(AbstractUnpackerBuilder):
     def _get_variants_map(self, spec: ValueSpec) -> str:
         variants_attr = self._get_variants_attr(spec)
         if spec.builder.is_nailed:
-            return f"{type_name(spec.builder.cls)}.{variants_attr}"
+            typ_name = spec.builder.get_type_name_identifier(spec.builder.cls)
+            return f"{typ_name}.{variants_attr}"
         else:
             return f"{spec.cls_attrs_name}.{variants_attr}"
 
@@ -266,11 +267,14 @@ class DiscriminatedUnionUnpackerBuilder(AbstractUnpackerBuilder):
         if self.discriminator.include_subtypes:
             spec.builder.ensure_object_imported(iter_all_subclasses)
             variant_names.extend(
-                f"*iter_all_subclasses({type_name(base_variant)})"
+                f"*iter_all_subclasses("
+                f"{spec.builder.get_type_name_identifier(base_variant)})"
                 for base_variant in base_variants
             )
         if self.discriminator.include_supertypes:
-            variant_names.extend(map(type_name, base_variants))
+            variant_names.extend(
+                map(spec.builder.get_type_name_identifier, base_variants)
+            )
         return variant_names
 
     def _get_variant_names_iterable(self, spec: ValueSpec) -> str:


Test patch:
diff --git a/tests/test_jsonschema/test_jsonschema_generation.py b/tests/test_jsonschema/test_jsonschema_generation.py
index 0456003..09f629b 100644
--- a/tests/test_jsonschema/test_jsonschema_generation.py
+++ b/tests/test_jsonschema/test_jsonschema_generation.py
@@ -73,7 +73,7 @@ from mashumaro.jsonschema.models import (
     JSONSchemaStringFormat,
 )
 from mashumaro.jsonschema.schema import UTC_OFFSET_PATTERN, EmptyJSONSchema
-from mashumaro.types import SerializationStrategy
+from mashumaro.types import Discriminator, SerializationStrategy
 from tests.entities import (
     CustomPath,
     GenericNamedTuple,
@@ -1243,3 +1243,134 @@ def test_jsonschema_with_additional_properties_schema():
         required=["x"],
     )
     assert build_json_schema(DataClass) == schema
+
+
+def test_jsonschema_with_discriminator_for_local_types():
+    @dataclass
+    class A:
+        value = "a"
+
+    @dataclass
+    class B:
+        value = "b"
+
+    @dataclass
+    class Main:
+        value: Annotated[
+            Union[A, B], Discriminator(field="value", include_supertypes=True)
+        ]
+
+    schema = JSONObjectSchema(
+        title="Main",
+        properties={
+            "value": JSONSchema(
+                anyOf=[
+                    JSONObjectSchema(
+                        type=JSONSchemaInstanceType.OBJECT,
+                        title="A",
+                        additionalProperties=False,
+                    ),
+                    JSONObjectSchema(
+                        type=JSONSchemaInstanceType.OBJECT,
+                        title="B",
+                        additionalProperties=False,
+                    ),
+                ],
+            ),
+        },
+        additionalProperties=False,
+        required=["value"],
+    )
+    assert build_json_schema(Main) == schema
+
+
+def test_jsonschema_with_discriminator_with_default_for_local_types():
+    @dataclass
+    class A:
+        value = "a"
+
+    @dataclass
+    class B:
+        value = "b"
+
+    @dataclass
+    class Main:
+        value: Annotated[
+            Union[A, B, None],
+            Discriminator(field="value", include_supertypes=True),
+        ] = None
+
+    schema = JSONObjectSchema(
+        title="Main",
+        properties={
+            "value": JSONSchema(
+                anyOf=[
+                    JSONObjectSchema(
+                        type=JSONSchemaInstanceType.OBJECT,
+                        title="A",
+                        additionalProperties=False,
+                    ),
+                    JSONObjectSchema(
+                        type=JSONSchemaInstanceType.OBJECT,
+                        title="B",
+                        additionalProperties=False,
+                    ),
+                    JSONSchema(
+                        type=JSONSchemaInstanceType.NULL,
+                    ),
+                ],
+                default=None,
+            ),
+        },
+        additionalProperties=False,
+    )
+    assert build_json_schema(Main) == schema
+
+
+def test_jsonschema_with_optional_discriminator_and_default_for_local_types():
+    @dataclass
+    class A:
+        value = "a"
+
+    @dataclass
+    class B:
+        value = "b"
+
+    @dataclass
+    class Main:
+        value: Optional[
+            Annotated[
+                Union[A, B],
+                Discriminator(field="value", include_supertypes=True),
+            ]
+        ] = None
+
+    schema = JSONObjectSchema(
+        title="Main",
+        properties={
+            "value": JSONSchema(
+                anyOf=[
+                    JSONSchema(
+                        anyOf=[
+                            JSONObjectSchema(
+                                type=JSONSchemaInstanceType.OBJECT,
+                                title="A",
+                                additionalProperties=False,
+                            ),
+                            JSONObjectSchema(
+                                type=JSONSchemaInstanceType.OBJECT,
+                                title="B",
+                                additionalProperties=False,
+                            ),
+                        ]
+                    ),
+                    JSONSchema(
+                        type=JSONSchemaInstanceType.NULL,
+                    ),
+                ],
+                default=None,
+            ),
+        },
+        additionalProperties=False,
+    )
+    assert build_json_schema(Main) == schema