diff --git a/mashumaro/core/meta/code/builder.py b/mashumaro/core/meta/code/builder.py index 1ef9f938..655cde76 100644 --- a/mashumaro/core/meta/code/builder.py +++ b/mashumaro/core/meta/code/builder.py @@ -45,6 +45,7 @@ is_hashable, is_init_var, is_literal, + is_local_type, is_named_tuple, is_optional, is_type_var_any, @@ -52,7 +53,7 @@ substitute_type_params, type_name, ) -from mashumaro.core.meta.types.common import FieldContext, NoneType, ValueSpec +from mashumaro.core.meta.types.common import FieldContext, NoneType, ValueSpec, clean_id from mashumaro.core.meta.types.pack import PackerRegistry from mashumaro.core.meta.types.unpack import ( SubtypeUnpackerBuilder, @@ -80,8 +81,6 @@ __POST_SERIALIZE__ = "__post_serialize__" __POST_DESERIALIZE__ = "__post_deserialize__" -_LOCALS_TYPE_VAR = "__locals_type_var__" - class InternalMethodName(str): _PREFIX = "__mashumaro_" @@ -306,8 +305,7 @@ def compile(self) -> None: print(f"{type_name(self.cls)}:") print(code) - localns = {**self.__dict__, _LOCALS_TYPE_VAR: self.cls} - exec(code, self.globals, localns) + exec(code, self.globals, self.__dict__) def evaluate_forward_ref( self, @@ -560,8 +558,9 @@ def _unpack_method_set_value( resolved_type_params=self.get_field_resolved_type_params(fname), ) - if "" in field_type: - field_type = _LOCALS_TYPE_VAR + if is_local_type(ftype): + field_type = clean_id(field_type) + self.ensure_object_imported(ftype, field_type) could_be_none = ( ftype in (typing.Any, type(None), None) diff --git a/mashumaro/core/meta/helpers.py b/mashumaro/core/meta/helpers.py index 2d27c44d..e556c3aa 100644 --- a/mashumaro/core/meta/helpers.py +++ b/mashumaro/core/meta/helpers.py @@ -387,6 +387,8 @@ def is_literal(typ: Type) -> bool: return type(typ) is typing._LiteralGenericAlias # type: ignore return False +def is_local_type(typ: Type) -> bool: + return "" in getattr(typ, "__qualname__", "") def not_none_type_arg( type_args: Tuple[Type, ...], diff --git a/mashumaro/core/meta/types/common.py b/mashumaro/core/meta/types/common.py index c5a1e282..bf6626d2 100644 --- a/mashumaro/core/meta/types/common.py +++ b/mashumaro/core/meta/types/common.py @@ -1,4 +1,5 @@ import collections.abc +import re import uuid from abc import ABC, abstractmethod from dataclasses import dataclass, field, replace @@ -302,8 +303,10 @@ def expr_or_maybe_none(spec: ValueSpec, new_expr: Expression) -> Expression: def random_hex() -> str: return str(uuid.uuid4().hex) +_PY_VALID_ID_RE = re.compile(r"\W|^(?=\d)") def clean_id(value: str) -> str: - for c in ".<>": - value = value.replace(c, "_") - return value + if not value: + return "_" + + return _PY_VALID_ID_RE.sub("_", value)