Skip to content

import unitxt fails on Python 3.14: get_fields reads __annotations__ from the class namespace (PEP 649) #1974

Description

@killua156

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions