Summary
import unitxt fails on Python 3.14. The dataclass machinery in src/unitxt/dataclass.py collects fields by reading __annotations__ out of the class-body namespace, which PEP 649 removed in 3.14. No fields get registered, so every annotated attribute is later rejected as an unexpected keyword argument.
Reproduced on unitxt 1.26.10 with Python 3.14.6 (linux-64, osx-64 and win-64). Python 3.10 through 3.13 are unaffected.
Reproduction
python3.14 -c "import unitxt"
File ".../unitxt/metric_utils.py", line 112, in <module>
RecursiveCopy(
field="prediction",
to_field="raw_prediction",
)
File ".../unitxt/dataclass.py", line 404, in custom_cls_init
original_init(self, *args, **kwargs)
[Previous line repeated 7 more times]
File ".../unitxt/dataclass.py", line 538, in __init__
raise UnexpectedArgumentError(
unitxt.dataclass.UnexpectedArgumentError: Unexpected keyword argument(s) {'field': 'prediction', 'to_field': 'raw_prediction'} for class RecursiveCopy.
This is an import-time failure, not a test-only one: unitxt/metric_utils.py constructs RecursiveCopy at module scope.
Root cause
get_fields in src/unitxt/dataclass.py reads annotations from the namespace dict handed to the metaclass:
annotations = {**attrs.get("__annotations__", {})}
Under PEP 649 the compiler no longer puts __annotations__ in the class-body namespace. It emits an annotate function instead and computes __annotations__ lazily on attribute access:
class Meta(type):
def __new__(mcs, name, bases, attrs):
print(sorted(attrs))
print(attrs.get("__annotations__", {}))
return super().__new__(mcs, name, bases, attrs)
class Base(metaclass=Meta):
field: str = None
to_field: str = None
Python 3.13 and earlier:
['__annotations__', '__module__', '__qualname__', 'field', 'to_field']
{'field': <class 'str'>, 'to_field': <class 'str'>}
Python 3.14.6:
['__annotate_func__', '__classdictcell__', '__firstlineno__', '__module__', '__qualname__', '__static_attributes__', 'field', 'to_field']
{}
So on 3.14 get_fields sees no annotations. InstanceFieldOperator declares field and to_field as annotated attributes, neither is registered as a field, and __init__ rejects both.
Note Base.__annotations__ still resolves correctly after the class exists. Only the namespace read is affected.
Suggested fix
Resolve annotations from the class rather than the namespace, for example via annotationlib.get_annotations() on 3.14+ with a fallback to attrs.get("__annotations__", {}) on older versions.
Impact
This blocks unitxt on Python 3.14 entirely. We hit it packaging unitxt for conda-forge (conda-forge/staged-recipes#33912) and have capped the recipe at python <3.14 until it is resolved.
Summary
import unitxtfails on Python 3.14. The dataclass machinery insrc/unitxt/dataclass.pycollects fields by reading__annotations__out of the class-body namespace, which PEP 649 removed in 3.14. No fields get registered, so every annotated attribute is later rejected as an unexpected keyword argument.Reproduced on unitxt 1.26.10 with Python 3.14.6 (linux-64, osx-64 and win-64). Python 3.10 through 3.13 are unaffected.
Reproduction
This is an import-time failure, not a test-only one:
unitxt/metric_utils.pyconstructsRecursiveCopyat module scope.Root cause
get_fieldsinsrc/unitxt/dataclass.pyreads annotations from the namespace dict handed to the metaclass:Under PEP 649 the compiler no longer puts
__annotations__in the class-body namespace. It emits an annotate function instead and computes__annotations__lazily on attribute access:Python 3.13 and earlier:
Python 3.14.6:
So on 3.14
get_fieldssees no annotations.InstanceFieldOperatordeclaresfieldandto_fieldas annotated attributes, neither is registered as a field, and__init__rejects both.Note
Base.__annotations__still resolves correctly after the class exists. Only the namespace read is affected.Suggested fix
Resolve annotations from the class rather than the namespace, for example via
annotationlib.get_annotations()on 3.14+ with a fallback toattrs.get("__annotations__", {})on older versions.Impact
This blocks unitxt on Python 3.14 entirely. We hit it packaging unitxt for conda-forge (conda-forge/staged-recipes#33912) and have capped the recipe at
python <3.14until it is resolved.