Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-mashumaro for 
openSUSE:Factory checked in at 2025-10-07 18:28:18
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-mashumaro (Old)
 and      /work/SRC/openSUSE:Factory/.python-mashumaro.new.11973 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-mashumaro"

Tue Oct  7 18:28:18 2025 rev:5 rq:1309470 version:3.17

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-mashumaro/python-mashumaro.changes        
2025-05-22 16:56:37.413288914 +0200
+++ 
/work/SRC/openSUSE:Factory/.python-mashumaro.new.11973/python-mashumaro.changes 
    2025-10-07 18:29:53.257309271 +0200
@@ -1,0 +2,16 @@
+Tue Oct  7 06:05:40 UTC 2025 - Johannes Kastl 
<[email protected]>
+
+- update to 3.17:
+  * Added support for Python 3.14 (#285)
+  * Improved generating JSON Schema references and titles for
+    generic dataclasses (#291)
+  * Fixed JSON Schema for a generic dataclass with a field type T
+    (#290)
+  * Fixed ignoring NotRequired when using from __future__ import
+    annotations (#292)
+  * Added support for user extra args in field_options function
+    (#286)
+  * Improved packaging by switching from setup.py to pyproject.toml
+    (#284)
+
+-------------------------------------------------------------------

Old:
----
  mashumaro-3.16.tar.gz

New:
----
  mashumaro-3.17.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-mashumaro.spec ++++++
--- /var/tmp/diff_new_pack.NI39vq/_old  2025-10-07 18:29:53.757330419 +0200
+++ /var/tmp/diff_new_pack.NI39vq/_new  2025-10-07 18:29:53.761330589 +0200
@@ -1,7 +1,7 @@
 #
 # spec file for package python-mashumaro
 #
-# Copyright (c) 2025 SUSE LLC
+# Copyright (c) 2025 SUSE LLC and contributors
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -18,14 +18,14 @@
 
 %{?sle15_python_module_pythons}
 Name:           python-mashumaro
-Version:        3.16
+Version:        3.17
 Release:        0
 Summary:        Fast and well tested serialization library
 License:        Apache-2.0
 URL:            https://github.com/Fatal1ty/mashumaro
 Source:         
https://github.com/Fatal1ty/mashumaro/archive/refs/tags/v%{version}.tar.gz#/mashumaro-%{version}.tar.gz
 BuildRequires:  %{python_module pip}
-BuildRequires:  %{python_module setuptools}
+BuildRequires:  %{python_module setuptools >= 77.0}
 BuildRequires:  %{python_module wheel}
 BuildRequires:  python-rpm-macros
 # Add (optional) runtime dependencies as BuildRequires,
@@ -39,7 +39,7 @@
 # SECTION test requirements
 BuildRequires:  %{python_module pytest >= 6.2.1}
 BuildRequires:  %{python_module ciso8601 >= 2.1.3}
-BuildRequires:  %{python_module pendulum >= 2.1.2 if %python-base < 3.13}
+BuildRequires:  %{python_module pendulum >= 2.1.2}
 BuildRequires:  %{python_module pytest-mock >= 3.5.1}
 BuildRequires:  %{python_module pytest-xdist >= 3.5.0}
 # /SECTION

++++++ mashumaro-3.16.tar.gz -> mashumaro-3.17.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mashumaro-3.16/.github/workflows/main.yml 
new/mashumaro-3.17/.github/workflows/main.yml
--- old/mashumaro-3.16/.github/workflows/main.yml       2025-05-20 
20:36:49.000000000 +0200
+++ new/mashumaro-3.17/.github/workflows/main.yml       2025-10-03 
23:05:12.000000000 +0200
@@ -13,8 +13,9 @@
     name: Code style tests
     runs-on: ubuntu-latest
     strategy:
+      fail-fast: false
       matrix:
-        python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
+        python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
     steps:
       - uses: actions/checkout@v4
       - name: Set up Python ${{ matrix.python-version }}
@@ -52,8 +53,9 @@
       - test-code-style
     runs-on: ubuntu-latest
     strategy:
+      fail-fast: false
       matrix:
-        python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
+        python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
     steps:
     - uses: actions/checkout@v4
     - name: Set up Python ${{ matrix.python-version }}
@@ -86,7 +88,9 @@
       - test-code-style
     runs-on: windows-latest
     strategy:
+      fail-fast: false
       matrix:
+        # TODO add 3.14 once msgpack support Python 3.14
         python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
     steps:
     - uses: actions/checkout@v4
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mashumaro-3.16/MANIFEST.in 
new/mashumaro-3.17/MANIFEST.in
--- old/mashumaro-3.16/MANIFEST.in      2025-05-20 20:36:49.000000000 +0200
+++ new/mashumaro-3.17/MANIFEST.in      2025-10-03 23:05:12.000000000 +0200
@@ -1 +1,2 @@
-recursive-include tests *
+graft tests
+recursive-exclude * *.py[co]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mashumaro-3.16/mashumaro/core/const.py 
new/mashumaro-3.17/mashumaro/core/const.py
--- old/mashumaro-3.16/mashumaro/core/const.py  2025-05-20 20:36:49.000000000 
+0200
+++ new/mashumaro-3.17/mashumaro/core/const.py  2025-10-03 23:05:12.000000000 
+0200
@@ -8,6 +8,7 @@
     "PY_311_MIN",
     "PY_312_MIN",
     "PY_313_MIN",
+    "PY_314_MIN",
     "Sentinel",
 ]
 
@@ -16,8 +17,10 @@
 PY_310 = sys.version_info.major == 3 and sys.version_info.minor == 10
 PY_311 = sys.version_info.major == 3 and sys.version_info.minor == 11
 PY_312 = sys.version_info.major == 3 and sys.version_info.minor == 12
-PY_313_MIN = sys.version_info.major == 3 and sys.version_info.minor >= 13
+PY_313 = sys.version_info.major == 3 and sys.version_info.minor == 13
+PY_314_MIN = sys.version_info.major == 3 and sys.version_info.minor >= 14
 
+PY_313_MIN = PY_313 or PY_314_MIN
 PY_312_MIN = PY_312 or PY_313_MIN
 PY_311_MIN = PY_311 or PY_312_MIN
 PY_310_MIN = PY_310 or PY_311_MIN
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mashumaro-3.16/mashumaro/core/meta/code/builder.py 
new/mashumaro-3.17/mashumaro/core/meta/code/builder.py
--- old/mashumaro-3.16/mashumaro/core/meta/code/builder.py      2025-05-20 
20:36:49.000000000 +0200
+++ new/mashumaro-3.17/mashumaro/core/meta/code/builder.py      2025-10-03 
23:05:12.000000000 +0200
@@ -2,6 +2,7 @@
 import importlib
 import inspect
 import math
+import sys
 import types
 import typing
 import uuid
@@ -31,11 +32,9 @@
 from mashumaro.core.helpers import ConfigValue
 from mashumaro.core.meta.code.lines import CodeLines
 from mashumaro.core.meta.helpers import (
-    evaluate_forward_ref,
     get_args,
     get_class_that_defines_field,
     get_class_that_defines_method,
-    get_forward_ref_referencing_globals,
     get_literal_values,
     get_name_error_name,
     get_type_annotations,
@@ -85,6 +84,11 @@
 )
 from mashumaro.types import Alias, Discriminator
 
+if sys.version_info >= (3, 14):
+    from annotationlib import get_annotations
+else:
+    from typing_extensions import get_annotations
+
 __PRE_SERIALIZE__ = "__pre_serialize__"
 __PRE_DESERIALIZE__ = "__pre_deserialize__"
 __POST_SERIALIZE__ = "__post_serialize__"
@@ -167,7 +171,7 @@
 
     @property
     def annotations(self) -> dict[str, typing.Any]:
-        return self.namespace.get("__annotations__", {})
+        return get_annotations(self.cls, eval_str=True)
 
     @property
     def is_nailed(self) -> bool:
@@ -329,16 +333,6 @@
             print(code)
         exec(code, self.globals, self.__dict__)
 
-    def evaluate_forward_ref(
-        self,
-        typ: typing.ForwardRef,
-        owner: typing.Optional[typing.Type],
-    ) -> typing.Optional[typing.Type]:
-        globalns = get_forward_ref_referencing_globals(
-            typ, owner, self.globals
-        )
-        return evaluate_forward_ref(typ, globalns, self.__dict__)
-
     def get_declared_hook(self, method_name: str) -> typing.Any:
         cls = get_class_that_defines_method(method_name, self.cls)
         if cls is not None and not is_dataclass_dict_mixin(cls):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mashumaro-3.16/mashumaro/core/meta/helpers.py 
new/mashumaro-3.17/mashumaro/core/meta/helpers.py
--- old/mashumaro-3.16/mashumaro/core/meta/helpers.py   2025-05-20 
20:36:49.000000000 +0200
+++ new/mashumaro-3.17/mashumaro/core/meta/helpers.py   2025-10-03 
23:05:12.000000000 +0200
@@ -2,7 +2,6 @@
 import enum
 import inspect
 import re
-import sys
 import types
 import typing
 from collections.abc import Callable, Hashable, Iterable, Iterator
@@ -34,7 +33,7 @@
     PY_310_MIN,
     PY_311_MIN,
     PY_312_MIN,
-    PY_313_MIN,
+    PY_314_MIN,
 )
 from mashumaro.dialect import Dialect
 
@@ -81,8 +80,6 @@
     "iter_all_subclasses",
     "is_hashable",
     "is_hashable_type",
-    "evaluate_forward_ref",
-    "get_forward_ref_referencing_globals",
     "is_type_alias_type",
 ]
 
@@ -289,7 +286,7 @@
 def is_special_typing_primitive(typ: Any) -> bool:
     try:
         issubclass(typ, object)
-        return False
+        return PY_314_MIN and issubclass(typ, typing.Union)  # type: 
ignore[arg-type]
     except TypeError:
         return True
 
@@ -756,42 +753,8 @@
 def str_to_forward_ref(
     annotation: str, module: Optional[types.ModuleType] = None
 ) -> ForwardRef:
-    return ForwardRef(annotation, module=module)
-
-
-def evaluate_forward_ref(
-    typ: ForwardRef, globalns: dict[str, Any], localns: dict[str, Any]
-) -> Optional[Type]:
-    if PY_313_MIN:
-        return typ._evaluate(
-            globalns, localns, type_params=(), recursive_guard=frozenset()
-        )  # type: ignore[call-arg]
-    else:
-        return typ._evaluate(
-            globalns, localns, recursive_guard=frozenset()
-        )  # type: ignore[call-arg]
-
-
-def get_forward_ref_referencing_globals(
-    referenced_type: ForwardRef,
-    referencing_object: Optional[Any] = None,
-    fallback: Optional[dict[str, Any]] = None,
-) -> dict[str, Any]:
-    if fallback is None:
-        fallback = {}
-    forward_module = getattr(referenced_type, "__forward_module__", None)
-    if not forward_module and referencing_object:
-        # We can't get the module in which ForwardRef's value is defined on
-        # Python < 3.10, ForwardRef evaluation might not work properly
-        # without this information, so we will consider the namespace of
-        # the module in which this ForwardRef is used as globalns.
-        return getattr(
-            sys.modules.get(referencing_object.__module__, None),
-            "__dict__",
-            fallback,
-        )
-    else:
-        return getattr(forward_module, "__dict__", fallback)
+    module_name = module.__name__ if module else None
+    return ForwardRef(annotation, module=module_name)
 
 
 def is_type_alias_type(typ: Type) -> bool:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mashumaro-3.16/mashumaro/core/meta/types/pack.py 
new/mashumaro-3.17/mashumaro/core/meta/types/pack.py
--- old/mashumaro-3.16/mashumaro/core/meta/types/pack.py        2025-05-20 
20:36:49.000000000 +0200
+++ new/mashumaro-3.17/mashumaro/core/meta/types/pack.py        2025-10-03 
23:05:12.000000000 +0200
@@ -3,6 +3,7 @@
 import ipaddress
 import os
 import re
+import sys
 import typing
 import uuid
 import zoneinfo
@@ -16,6 +17,7 @@
 from typing import Any, ForwardRef, Optional, Tuple, Union
 
 import typing_extensions
+from typing_extensions import NotRequired
 
 from mashumaro.core.const import PY_311_MIN
 from mashumaro.core.meta.code.lines import CodeLines
@@ -75,6 +77,13 @@
     SerializationStrategy,
 )
 
+if sys.version_info >= (3, 14):
+    from typing import evaluate_forward_ref
+
+    from annotationlib import get_annotations
+else:
+    from typing_extensions import evaluate_forward_ref, get_annotations
+
 __all__ = ["PackerRegistry"]
 
 
@@ -94,9 +103,7 @@
     except (KeyError, ValueError):
         value_type = Any
     if isinstance(value_type, ForwardRef):
-        value_type = spec.builder.evaluate_forward_ref(
-            value_type, spec.origin_type
-        )
+        value_type = evaluate_forward_ref(value_type)
     value_type = substitute_type_params(
         value_type,  # type: ignore
         resolve_type_params(strategy_type, get_args(spec.type))[strategy_type],
@@ -191,9 +198,7 @@
     if is_self(value_type):
         return f"{spec.expression}._serialize()"
     if isinstance(value_type, ForwardRef):
-        value_type = spec.builder.evaluate_forward_ref(
-            value_type, spec.origin_type
-        )
+        value_type = evaluate_forward_ref(value_type)
     value_type = substitute_type_params(
         value_type,
         resolve_type_params(spec.origin_type, get_args(spec.type))[
@@ -539,9 +544,7 @@
         elif is_type_var_tuple(spec.type):
             return PackerRegistry.get(spec.copy(type=tuple[Any, ...]))
         elif isinstance(spec.type, ForwardRef):
-            evaluated = spec.builder.evaluate_forward_ref(
-                spec.type, spec.owner
-            )
+            evaluated = evaluate_forward_ref(spec.type)
             if evaluated is not None:
                 return PackerRegistry.get(spec.copy(type=evaluated))
         elif is_type_alias_type(spec.type):
@@ -674,7 +677,7 @@
     ]
     annotations = {
         k: resolved.get(v, v)
-        for k, v in getattr(spec.origin_type, "__annotations__", {}).items()
+        for k, v in get_annotations(spec.origin_type, eval_str=True).items()
     }
     fields = getattr(spec.type, "_fields", ())
     packers = []
@@ -716,11 +719,20 @@
     ]
     annotations = {
         k: resolved.get(v, v)
-        for k, v in spec.origin_type.__annotations__.items()
+        for k, v in get_annotations(spec.origin_type, eval_str=True).items()
     }
     all_keys = list(annotations.keys())
-    required_keys = getattr(spec.type, "__required_keys__", all_keys)
-    optional_keys = getattr(spec.type, "__optional_keys__", [])
+    required_keys = set(getattr(spec.type, "__required_keys__", all_keys))
+    optional_keys = set(getattr(spec.type, "__optional_keys__", []))
+
+    # workaround for https://github.com/python/cpython/issues/97727
+    for key, annotation in annotations.items():
+        if isinstance(annotation, ForwardRef):
+            annotation = evaluate_forward_ref(annotation)
+            if get_type_origin(annotation) is NotRequired:
+                required_keys.discard(key)
+                optional_keys.add(key)
+
     lines = CodeLines()
     method_name = (
         f"__pack_typed_dict_{spec.builder.cls.__name__}_"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mashumaro-3.16/mashumaro/core/meta/types/unpack.py 
new/mashumaro-3.17/mashumaro/core/meta/types/unpack.py
--- old/mashumaro-3.16/mashumaro/core/meta/types/unpack.py      2025-05-20 
20:36:49.000000000 +0200
+++ new/mashumaro-3.17/mashumaro/core/meta/types/unpack.py      2025-10-03 
23:05:12.000000000 +0200
@@ -6,6 +6,7 @@
 import os
 import pathlib
 import re
+import sys
 import types
 import typing
 import uuid
@@ -27,6 +28,7 @@
 from typing import Any, ForwardRef, Optional, Tuple, Union
 
 import typing_extensions
+from typing_extensions import NotRequired
 
 from mashumaro.core.const import PY_311_MIN
 from mashumaro.core.helpers import parse_timezone
@@ -36,6 +38,7 @@
     get_class_that_defines_method,
     get_function_arg_annotation,
     get_literal_values,
+    get_type_origin,
     get_type_var_default,
     is_final,
     is_generic,
@@ -92,6 +95,13 @@
     SerializationStrategy,
 )
 
+if sys.version_info >= (3, 14):
+    from typing import evaluate_forward_ref
+
+    from annotationlib import get_annotations
+else:
+    from typing_extensions import evaluate_forward_ref, get_annotations
+
 try:
     import ciso8601
 except ImportError:  # pragma: no cover
@@ -561,9 +571,7 @@
     except (KeyError, ValueError):
         value_type = Any
     if isinstance(value_type, ForwardRef):
-        value_type = spec.builder.evaluate_forward_ref(
-            value_type, spec.origin_type
-        )
+        value_type = evaluate_forward_ref(value_type)
     value_type = substitute_type_params(
         value_type,  # type: ignore
         resolve_type_params(strategy_type, get_args(spec.type))[strategy_type],
@@ -649,9 +657,7 @@
             f"._deserialize({spec.expression})"
         )
     if isinstance(value_type, ForwardRef):
-        value_type = spec.builder.evaluate_forward_ref(
-            value_type, spec.origin_type
-        )
+        value_type = evaluate_forward_ref(value_type)
     value_type = substitute_type_params(
         value_type,
         resolve_type_params(spec.origin_type, get_args(spec.type))[
@@ -867,9 +873,7 @@
         elif is_type_var_tuple(spec.type):
             return UnpackerRegistry.get(spec.copy(type=tuple[Any, ...]))
         elif isinstance(spec.type, ForwardRef):
-            evaluated = spec.builder.evaluate_forward_ref(
-                spec.type, spec.owner
-            )
+            evaluated = evaluate_forward_ref(spec.type)
             if evaluated is not None:
                 return UnpackerRegistry.get(spec.copy(type=evaluated))
         elif is_type_alias_type(spec.type):
@@ -1063,7 +1067,7 @@
     ]
     annotations = {
         k: resolved.get(v, v)
-        for k, v in getattr(spec.origin_type, "__annotations__", {}).items()
+        for k, v in get_annotations(spec.origin_type, eval_str=True).items()
     }
     fields = getattr(spec.type, "_fields", ())
     defaults = getattr(spec.type, "_field_defaults", {})
@@ -1151,11 +1155,20 @@
     ]
     annotations = {
         k: resolved.get(v, v)
-        for k, v in spec.origin_type.__annotations__.items()
+        for k, v in get_annotations(spec.origin_type, eval_str=True).items()
     }
     all_keys = list(annotations.keys())
-    required_keys = getattr(spec.type, "__required_keys__", all_keys)
-    optional_keys = getattr(spec.type, "__optional_keys__", [])
+    required_keys = set(getattr(spec.type, "__required_keys__", all_keys))
+    optional_keys = set(getattr(spec.type, "__optional_keys__", []))
+
+    # workaround for https://github.com/python/cpython/issues/97727
+    for key, annotation in annotations.items():
+        if isinstance(annotation, ForwardRef):
+            annotation = evaluate_forward_ref(annotation)
+            if get_type_origin(annotation) is NotRequired:
+                required_keys.discard(key)
+                optional_keys.add(key)
+
     lines = CodeLines()
     method_name = (
         f"__unpack_typed_dict_{spec.builder.cls.__name__}_"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mashumaro-3.16/mashumaro/helper.py 
new/mashumaro-3.17/mashumaro/helper.py
--- old/mashumaro-3.16/mashumaro/helper.py      2025-05-20 20:36:49.000000000 
+0200
+++ new/mashumaro-3.17/mashumaro/helper.py      2025-10-03 23:05:12.000000000 
+0200
@@ -36,12 +36,14 @@
     ] = None,
     serialization_strategy: Optional[SerializationStrategy] = None,
     alias: Optional[str] = None,
+    **kwargs: Any,
 ) -> dict[str, Any]:
     return {
         "serialize": serialize,
         "deserialize": deserialize,
         "serialization_strategy": serialization_strategy,
         "alias": alias,
+        **kwargs,
     }
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mashumaro-3.16/mashumaro/jsonschema/schema.py 
new/mashumaro-3.17/mashumaro/jsonschema/schema.py
--- old/mashumaro-3.16/mashumaro/jsonschema/schema.py   2025-05-20 
20:36:49.000000000 +0200
+++ new/mashumaro-3.17/mashumaro/jsonschema/schema.py   2025-10-03 
23:05:12.000000000 +0200
@@ -1,10 +1,11 @@
 import datetime
 import ipaddress
 import os
+import sys
 import warnings
 from base64 import encodebytes
 from collections import ChainMap, Counter, deque
-from collections.abc import (
+from collections.abc import (  # type: ignore[attr-defined]
     ByteString,
     Callable,
     Collection,
@@ -22,15 +23,13 @@
 from uuid import UUID
 from zoneinfo import ZoneInfo
 
-from typing_extensions import TypeAlias
+from typing_extensions import NotRequired, TypeAlias
 
 from mashumaro.config import BaseConfig
 from mashumaro.core.const import PY_311_MIN
 from mashumaro.core.meta.code.builder import CodeBuilder
 from mashumaro.core.meta.helpers import (
-    evaluate_forward_ref,
     get_args,
-    get_forward_ref_referencing_globals,
     get_function_return_annotation,
     get_literal_values,
     get_type_origin,
@@ -52,7 +51,7 @@
     resolve_type_params,
     type_name,
 )
-from mashumaro.core.meta.types.common import NoneType
+from mashumaro.core.meta.types.common import NoneType, clean_id
 from mashumaro.helper import pass_through
 from mashumaro.jsonschema.annotations import (
     Annotation,
@@ -94,6 +93,13 @@
 except ImportError:  # pragma: no cover
     from mashumaro.mixins.json import DataClassJSONMixin  # type: ignore
 
+if sys.version_info >= (3, 14):
+    from typing import evaluate_forward_ref
+
+    from annotationlib import get_annotations
+else:
+    from typing_extensions import evaluate_forward_ref, get_annotations
+
 
 UTC_OFFSET_PATTERN = r"^UTC([+-][0-2][0-9]:[0-5][0-9])?$"
 
@@ -143,14 +149,11 @@
     def derive(self, **changes: Any) -> "Instance":
         new_type = changes.get("type")
         if isinstance(new_type, ForwardRef):
-            changes["type"] = evaluate_forward_ref(
-                new_type,
-                get_forward_ref_referencing_globals(new_type, self.type),
-                self.__dict__,
-            )
+            changes["type"] = evaluate_forward_ref(new_type)
         new_instance = replace(self, **changes)
         if is_dataclass(self.origin_type):
             new_instance.__owner_builder = self.__self_builder
+            new_instance.update_type(new_instance.type)
         return new_instance
 
     def __post_init__(self) -> None:
@@ -296,7 +299,9 @@
     return schema
 
 
-def _default(f_type: Type, f_value: Any, config_cls: Type[BaseConfig]) -> Any:
+def _default(
+    f_type: Optional[Type], f_value: Any, config_cls: Type[BaseConfig]
+) -> Any:
     @dataclass
     class CC(DataClassJSONMixin):
         x: f_type = f_value  # type: ignore
@@ -351,9 +356,14 @@
 def on_dataclass(instance: Instance, ctx: Context) -> Optional[JSONSchema]:
     # TODO: Self references might not work
     if is_dataclass(instance.origin_type):
+        if ctx.all_refs:
+            title = clean_id(type_name(instance.type, short=True))
+            title = title.strip("_")
+        else:
+            title = instance.origin_type.__name__
         jsonschema_config = instance.get_self_config().json_schema
         schema = JSONObjectSchema(
-            title=instance.origin_type.__name__,
+            title=title,
             additionalProperties=jsonschema_config.get(
                 "additionalProperties", False
             ),
@@ -385,11 +395,9 @@
         if required:
             schema.required = required
         if ctx.all_refs:
-            ctx.definitions[instance.origin_type.__name__] = schema
+            ctx.definitions[title] = schema
             ref_prefix = ctx.ref_prefix or ctx.dialect.definitions_root_pointer
-            return JSONSchema(
-                reference=f"{ref_prefix}/{instance.origin_type.__name__}"
-            )
+            return JSONSchema(reference=f"{ref_prefix}/{title}")
         else:
             return schema
 
@@ -461,11 +469,7 @@
     elif is_readonly(instance.type):
         return get_schema(instance.derive(type=args[0]), ctx)
     elif isinstance(instance.type, ForwardRef):
-        evaluated = evaluate_forward_ref(
-            instance.type,
-            get_forward_ref_referencing_globals(instance.type),
-            None,
-        )
+        evaluated = evaluate_forward_ref(instance.type)
         if evaluated is not None:
             return get_schema(instance.derive(type=evaluated), ctx)
 
@@ -639,8 +643,8 @@
     )[instance.origin_type]
     annotations = {
         k: resolved.get(v, v)
-        for k, v in getattr(
-            instance.origin_type, "__annotations__", {}
+        for k, v in get_annotations(
+            instance.origin_type, eval_str=True
         ).items()
     }
     fields = getattr(instance.type, "_fields", ())
@@ -685,10 +689,20 @@
     )[instance.origin_type]
     annotations = {
         k: resolved.get(v, v)
-        for k, v in instance.origin_type.__annotations__.items()
+        for k, v in get_annotations(
+            instance.origin_type, eval_str=True
+        ).items()
     }
     all_keys = list(annotations.keys())
-    required_keys = getattr(instance.type, "__required_keys__", all_keys)
+    required_keys = set(getattr(instance.type, "__required_keys__", all_keys))
+
+    # workaround for https://github.com/python/cpython/issues/97727
+    for key, annotation in annotations.items():
+        if isinstance(annotation, ForwardRef):
+            annotation = evaluate_forward_ref(annotation)
+            if get_type_origin(annotation) is NotRequired:
+                required_keys.discard(key)
+
     return JSONObjectSchema(
         properties={
             key: get_schema(instance.derive(type=annotations[key]), ctx)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mashumaro-3.16/pyproject.toml 
new/mashumaro-3.17/pyproject.toml
--- old/mashumaro-3.16/pyproject.toml   2025-05-20 20:36:49.000000000 +0200
+++ new/mashumaro-3.17/pyproject.toml   2025-10-03 23:05:12.000000000 +0200
@@ -1,3 +1,52 @@
+[build-system]
+build-backend = "setuptools.build_meta"
+requires = ["setuptools>=77.0"]
+
+[project]
+name = "mashumaro"
+version = "3.17"
+license = "Apache-2.0"
+description = "Fast and well tested serialization library"
+readme = "README.md"
+authors = [{ name = "Alexander Tikhonov", email = "[email protected]" }]
+requires-python = ">=3.9"
+classifiers = [
+    "Intended Audience :: Developers",
+    "Operating System :: OS Independent",
+    "Programming Language :: Python :: 3 :: Only",
+    "Programming Language :: Python :: 3.9",
+    "Programming Language :: Python :: 3.10",
+    "Programming Language :: Python :: 3.11",
+    "Programming Language :: Python :: 3.12",
+    "Programming Language :: Python :: 3.13",
+    "Programming Language :: Python :: 3.14",
+    "Development Status :: 5 - Production/Stable",
+]
+dependencies = [
+    "typing_extensions>=4.14.0",
+]
+
+[project.urls]
+Homepage = "https://github.com/Fatal1ty/mashumaro";
+
+[project.optional-dependencies]
+orjson = ["orjson"]
+msgpack = ["msgpack>=0.5.6"]
+yaml = ["pyyaml>=3.13"]
+toml = [
+    "tomli-w>=1.0",
+    "tomli>=1.1.0;python_version<'3.11'",
+]
+
+[tool.setuptools.packages.find]
+include = ["mashumaro*"]
+
+[tool.setuptools.package-data]
+mashumaro = [
+    "py.typed",
+    "mixins/orjson.pyi",
+]
+
 [tool.mypy]
 ignore_missing_imports = true
 disallow_untyped_defs = true
@@ -34,9 +83,6 @@
 [tool.ruff]
 line-length = 79
 
-[tool.coverage.run]
-omit = ["setup.py"]
-
 [tool.coverage.report]
 exclude_lines = ["pragma: no cover", "@overload", "@abstractmethod"]
 ignore_errors = true
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mashumaro-3.16/requirements-dev.txt 
new/mashumaro-3.17/requirements-dev.txt
--- old/mashumaro-3.16/requirements-dev.txt     2025-05-20 20:36:49.000000000 
+0200
+++ new/mashumaro-3.17/requirements-dev.txt     2025-10-03 23:05:12.000000000 
+0200
@@ -1,5 +1,5 @@
 # extra
-msgpack>=0.5.6
+msgpack>=0.5.6;sys_platform != 'win32' or (sys_platform == 'win32' and 
python_version<'3.14')
 pyyaml>=3.13
 tomli-w>=1.0
 tomli>=1.1.0;python_version<'3.11'
@@ -19,14 +19,14 @@
 
 # third party features
 ciso8601>=2.1.3
-pendulum>=2.1.2;python_version<'3.13'
+pendulum>=2.1.2
 
 # benchmark
 pyperf>=2.6.1
 termtables>=0.2.3
 pytablewriter[html]>=0.58.0
 cattrs==24.1.2
-pydantic==2.9.2
+pydantic==2.9.2;python_version<'3.14'  # see 
https://github.com/pydantic/pydantic/issues/11613
 dacite==1.7.0  # see 
https://github.com/konradhalas/dacite/issues/236#issuecomment-1613987368
 marshmallow>=3.19.0
 dataclasses-json==0.6.7
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mashumaro-3.16/setup.py new/mashumaro-3.17/setup.py
--- old/mashumaro-3.16/setup.py 2025-05-20 20:36:49.000000000 +0200
+++ new/mashumaro-3.17/setup.py 1970-01-01 01:00:00.000000000 +0100
@@ -1,43 +0,0 @@
-#!/usr/bin/env python
-
-from setuptools import find_packages, setup
-
-setup(
-    name="mashumaro",
-    version="3.16",
-    description="Fast and well tested serialization library",
-    long_description=open("README.md", encoding="utf8").read(),
-    long_description_content_type="text/markdown",
-    platforms="all",
-    classifiers=[
-        "License :: OSI Approved :: Apache Software License",
-        "Intended Audience :: Developers",
-        "Programming Language :: Python :: 3 :: Only",
-        "Programming Language :: Python :: 3.9",
-        "Programming Language :: Python :: 3.10",
-        "Programming Language :: Python :: 3.11",
-        "Programming Language :: Python :: 3.12",
-        "Programming Language :: Python :: 3.13",
-        "Development Status :: 5 - Production/Stable",
-    ],
-    license="Apache License, Version 2.0",
-    author="Alexander Tikhonov",
-    author_email="[email protected]",
-    url="https://github.com/Fatal1ty/mashumaro";,
-    packages=find_packages(include=("mashumaro", "mashumaro.*")),
-    package_data={"mashumaro": ["py.typed", "mixins/orjson.pyi"]},
-    python_requires=">=3.9",
-    install_requires=[
-        "typing_extensions>=4.1.0",
-    ],
-    extras_require={
-        "orjson": ["orjson"],
-        "msgpack": ["msgpack>=0.5.6"],
-        "yaml": ["pyyaml>=3.13"],
-        "toml": [
-            "tomli-w>=1.0",
-            "tomli>=1.1.0;python_version<'3.11'",
-        ],
-    },
-    zip_safe=False,
-)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mashumaro-3.16/tests/conftest.py 
new/mashumaro-3.17/tests/conftest.py
--- old/mashumaro-3.16/tests/conftest.py        2025-05-20 20:36:49.000000000 
+0200
+++ new/mashumaro-3.17/tests/conftest.py        2025-10-03 23:05:12.000000000 
+0200
@@ -1,6 +1,6 @@
 from unittest.mock import patch
 
-from mashumaro.core.const import PY_312_MIN, PY_313_MIN
+from mashumaro.core.const import PY_312_MIN
 
 if not PY_312_MIN:
     collect_ignore = [
@@ -9,15 +9,6 @@
         "test_recursive_union.py",
     ]
 
-if PY_313_MIN:
-    collect_ignore = [
-        "test_codecs/test_orjson_codec.py",
-        "test_discriminated_unions/test_dialects.py",
-        "test_orjson.py",
-        "test_pep_563.py",
-        "test_self.py",
-    ]
-
 add_unpack_method = patch(
     "mashumaro.core.meta.code.builder.CodeBuilder.add_unpack_method",
     lambda *args, **kwargs: ...,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/mashumaro-3.16/tests/test_jsonschema/test_jsonschema_generation.py 
new/mashumaro-3.17/tests/test_jsonschema/test_jsonschema_generation.py
--- old/mashumaro-3.16/tests/test_jsonschema/test_jsonschema_generation.py      
2025-05-20 20:36:49.000000000 +0200
+++ new/mashumaro-3.17/tests/test_jsonschema/test_jsonschema_generation.py      
2025-10-03 23:05:12.000000000 +0200
@@ -32,6 +32,7 @@
     OrderedDict,
     Sequence,
     Tuple,
+    TypeVar,
     Union,
 )
 from uuid import UUID
@@ -143,7 +144,7 @@
 
 def test_jsonschema_for_dataclass():
     @dataclass
-    class DataClass:
+    class MyClass:
         a: int
         b: float = 3.14
         c: Optional[int] = field(default=None, metadata={"alias": "cc"})
@@ -156,8 +157,8 @@
         class Config:
             aliases = {"a": "aa", "d": "dd"}
 
-    schema = JSONObjectSchema(
-        title="DataClass",
+    assert build_json_schema(MyClass) == JSONObjectSchema(
+        title="MyClass",
         properties={
             "aa": JSONSchema(type=JSONSchemaInstanceType.INTEGER),
             "b": JSONSchema(type=JSONSchemaInstanceType.NUMBER, default=3.14),
@@ -177,9 +178,35 @@
         additionalProperties=False,
         required=["aa"],
     )
-    assert build_json_schema(DataClass) == schema
-    assert build_json_schema(DataClass, all_refs=True) == JSONSchema(
-        reference="#/$defs/DataClass", definitions={"DataClass": schema}
+    assert build_json_schema(MyClass, all_refs=True) == JSONSchema(
+        reference="#/$defs/test_jsonschema_for_dataclass__locals__MyClass",
+        definitions={
+            "test_jsonschema_for_dataclass__locals__MyClass": JSONObjectSchema(
+                title="test_jsonschema_for_dataclass__locals__MyClass",
+                properties={
+                    "aa": JSONSchema(type=JSONSchemaInstanceType.INTEGER),
+                    "b": JSONSchema(
+                        type=JSONSchemaInstanceType.NUMBER, default=3.14
+                    ),
+                    "cc": JSONSchema(
+                        anyOf=[
+                            JSONSchema(type=JSONSchemaInstanceType.INTEGER),
+                            JSONSchema(type=JSONSchemaInstanceType.NULL),
+                        ],
+                        default=None,
+                    ),
+                    "dd": JSONSchema(
+                        type=JSONSchemaInstanceType.STRING, default=""
+                    ),
+                    "f": JSONArraySchema(
+                        items=JSONSchema(type=JSONSchemaInstanceType.INTEGER),
+                        description="description for f",
+                    ),
+                },
+                additionalProperties=False,
+                required=["aa"],
+            )
+        },
     )
 
 
@@ -1156,7 +1183,12 @@
     class DataClass:
         pass
 
-    schema = {"$ref": "#/components/responses/DataClass"}
+    schema = {
+        "$ref": (
+            "#/components/responses/"
+            "test_jsonschema_with_ref_prefix__locals__DataClass"
+        )
+    }
     assert (
         build_json_schema(
             List[DataClass], all_refs=True, ref_prefix="#/components/responses"
@@ -1443,3 +1475,72 @@
         required=["x", "y"],
         additionalProperties=False,
     )
+
+
+def test_jsonschema_for_generic_dataclass():
+    T = TypeVar("T")
+
+    @dataclass
+    class MyClass(Generic[T]):
+        x: T
+        y: list[T]
+
+    assert build_json_schema(MyClass) == JSONObjectSchema(
+        title="MyClass",
+        properties={
+            "x": EmptyJSONSchema(),
+            "y": JSONArraySchema(),
+        },
+        additionalProperties=False,
+        required=["x", "y"],
+    )
+    assert build_json_schema(MyClass[int]) == JSONObjectSchema(
+        title="MyClass",
+        properties={
+            "x": JSONSchema(type=JSONSchemaInstanceType.INTEGER),
+            "y": JSONArraySchema(
+                items=JSONSchema(type=JSONSchemaInstanceType.INTEGER)
+            ),
+        },
+        additionalProperties=False,
+        required=["x", "y"],
+    )
+
+    @dataclass
+    class MyClass2(Generic[T]):
+        z: MyClass[T]
+
+    assert build_json_schema(MyClass2) == JSONObjectSchema(
+        title="MyClass2",
+        properties={
+            "z": JSONObjectSchema(
+                title="MyClass",
+                properties={
+                    "x": EmptyJSONSchema(),
+                    "y": JSONArraySchema(),
+                },
+                additionalProperties=False,
+                required=["x", "y"],
+            )
+        },
+        additionalProperties=False,
+        required=["z"],
+    )
+    assert build_json_schema(MyClass2[str]) == JSONObjectSchema(
+        title="MyClass2",
+        properties={
+            "z": JSONObjectSchema(
+                title="MyClass",
+                properties={
+                    "x": JSONSchema(type=JSONSchemaInstanceType.STRING),
+                    "y": JSONArraySchema(
+                        items=JSONSchema(type=JSONSchemaInstanceType.STRING)
+                    ),
+                },
+                additionalProperties=False,
+                required=["x", "y"],
+            )
+        },
+        additionalProperties=False,
+        required=["z"],
+    )
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mashumaro-3.16/tests/test_metadata_options.py 
new/mashumaro-3.17/tests/test_metadata_options.py
--- old/mashumaro-3.16/tests/test_metadata_options.py   2025-05-20 
20:36:49.000000000 +0200
+++ new/mashumaro-3.17/tests/test_metadata_options.py   2025-10-03 
23:05:12.000000000 +0200
@@ -7,7 +7,6 @@
 import pytest
 
 from mashumaro import DataClassDictMixin
-from mashumaro.core.const import PY_312_MIN, PY_313_MIN
 from mashumaro.exceptions import (
     UnserializableField,
     UnsupportedDeserializationEngine,
@@ -58,7 +57,6 @@
     assert instance == should_be
 
 
[email protected](PY_313_MIN, reason="pendulum doesn't install on 3.13")
 def test_pendulum_datetime_parser():
     @dataclass
     class DataClass(DataClassDictMixin):
@@ -69,7 +67,6 @@
     assert instance == should_be
 
 
[email protected](PY_313_MIN, reason="pendulum doesn't install on 3.13")
 def test_pendulum_date_parser():
     @dataclass
     class DataClass(DataClassDictMixin):
@@ -80,7 +77,6 @@
     assert instance == should_be
 
 
[email protected](PY_313_MIN, reason="pendulum doesn't install on 3.13")
 def test_pendulum_time_parser():
     @dataclass
     class DataClass(DataClassDictMixin):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/mashumaro-3.16/tests/test_not_required_with_future_annotations.py 
new/mashumaro-3.17/tests/test_not_required_with_future_annotations.py
--- old/mashumaro-3.16/tests/test_not_required_with_future_annotations.py       
1970-01-01 01:00:00.000000000 +0100
+++ new/mashumaro-3.17/tests/test_not_required_with_future_annotations.py       
2025-10-03 23:05:12.000000000 +0200
@@ -0,0 +1,56 @@
+from __future__ import annotations
+
+from dataclasses import dataclass
+from typing import TypedDict
+
+from typing_extensions import NotRequired
+
+from mashumaro import DataClassDictMixin
+from mashumaro.codecs.basic import decode, encode
+from mashumaro.jsonschema import build_json_schema
+
+
+class MyDict(TypedDict):
+    a: str
+    b: NotRequired[int]
+
+
+@dataclass
+class MyDataClass(DataClassDictMixin):
+    data: MyDict
+
+
+def test_typeddict_with_not_required_and_future_annotations():
+    # test workaround for https://github.com/Fatal1ty/mashumaro/issues/292
+    assert decode({"a": "test", "b": 42}, MyDict) == {"a": "test", "b": 42}
+    assert decode({"a": "test", "b": "42"}, MyDict) == {"a": "test", "b": 42}
+    assert decode({"a": "test"}, MyDict) == {"a": "test"}
+
+    assert encode(MyDict(a="test"), MyDict) == {"a": "test"}
+    assert encode({"a": "test"}, MyDict) == {"a": "test"}
+    assert encode(MyDict(a="test", b=42), MyDict) == {"a": "test", "b": 42}
+
+    assert MyDataClass(MyDict(a="test", b=42)).to_dict() == {
+        "data": {"a": "test", "b": 42}
+    }
+    assert MyDataClass(MyDict(a="test")).to_dict() == {"data": {"a": "test"}}
+
+    assert MyDataClass.from_dict(
+        {"data": {"a": "test", "b": 42}}
+    ) == MyDataClass(MyDict(a="test", b=42))
+    assert MyDataClass.from_dict({"data": {"a": "test"}}) == MyDataClass(
+        MyDict(a="test")
+    )
+
+
+def test_jsonschema_for_not_required_and_future_annotations():
+    schema = build_json_schema(MyDict).to_dict()
+    assert schema == {
+        "type": "object",
+        "properties": {
+            "a": {"type": "string"},
+            "b": {"type": "integer"},
+        },
+        "required": ["a"],
+        "additionalProperties": False,
+    }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mashumaro-3.16/tests/test_pep_563.py 
new/mashumaro-3.17/tests/test_pep_563.py
--- old/mashumaro-3.16/tests/test_pep_563.py    2025-05-20 20:36:49.000000000 
+0200
+++ new/mashumaro-3.17/tests/test_pep_563.py    2025-10-03 23:05:12.000000000 
+0200
@@ -1,7 +1,7 @@
 from __future__ import annotations
 
 from dataclasses import dataclass
-from typing import Dict
+from typing import Any, Dict, Optional
 
 import msgpack
 import orjson
@@ -13,6 +13,7 @@
 from mashumaro.exceptions import UnresolvedTypeReferenceError
 from mashumaro.mixins.msgpack import DataClassMessagePackMixin
 from mashumaro.mixins.orjson import DataClassORJSONMixin
+from mashumaro.types import SerializationStrategy
 
 from .conftest import add_unpack_method
 
@@ -290,3 +291,23 @@
     dumped = orjson.dumps({"a": {"b": 123000}, "x": 456000})
     assert instance.to_jsonb(encoder=encoder) == dumped
     assert A3ORJSON.from_json(dumped, decoder=decoder) == instance
+
+
+def test_postponed_serialization_strategy() -> None:
+    class Strategy(SerializationStrategy, use_annotations=True):
+        def serialize(self, value) -> dict[str, Any]:
+            return {"a": value}
+
+        def deserialize(self, value: dict[str, Any]):
+            return value.get("a")
+
+    @dataclass
+    class MyDataClass(DataClassDictMixin):
+        x: Optional[int]
+
+        class Config(BaseConfig):
+            serialization_strategy = {int: Strategy()}
+
+    obj = MyDataClass(x=2)
+    assert obj.to_dict() == {"x": {"a": 2}}
+    assert MyDataClass.from_dict({"x": {"a": 2}}) == obj

Reply via email to