Skip to content

Fix importing unitxt on Python 3.14 and 3.15 - #1992

Open
cristianchiriac wants to merge 1 commit into
IBM:mainfrom
cristianchiriac:fix/python-3.15-compat
Open

cristianchiriac wants to merge 1 commit into
IBM:mainfrom
cristianchiriac:fix/python-3.15-compat

Conversation

@cristianchiriac

Copy link
Copy Markdown

import unitxt fails on Python 3.14 and 3.15, and CI doesn't catch it because it only runs older versions.

Python 3.14 (PEP 649/749): a class body no longer puts __annotations__ in the class namespace, only an __annotate__ function. get_fields() read attrs.get("__annotations__", {}), so no fields were found. The first Dataclass built with keyword arguments then raised at import:

unitxt.dataclass.UnexpectedArgumentError: Unexpected keyword argument(s) {'field': 'prediction', 'to_field': 'raw_prediction'} for class RecursiveCopy.
Should be one of: []
  • get_fields() now uses _namespace_annotations(attrs). It keeps __annotations__ when the namespace has it (all versions before 3.14), and otherwise uses annotationlib.get_annotate_from_class_namespace() + call_annotate_function(..., Format.FORWARDREF), the documented way for metaclasses.
  • Dataclass.to_dict(classes=...) read cls.__annotations__, which no longer resolves through an instance on 3.14. It now uses inspect.get_annotations() on the class (falling back to the class __dict__ before 3.10).

Python 3.15:

  • dataclasses._MISSING_TYPE is gone (MISSING became a PEP 661 sentinel), so update_init_signature compares with dataclasses.MISSING.
  • typing._eval_type() now requires type_params, so eval_forward_ref passes type_params=() on 3.13+. (3.13/3.14 accept it.)

Tests:

  • test_fields_from_annotations: fields of a base and a child class are collected, and keyword init works.
  • test_eval_forward_ref: plain and custom forward refs.

Results for pytest tests/library/test_dataclass.py tests/library/test_type_utils.py (pip install -e ., Docker python:<v>):

Python before after
3.9 59 passed
3.12 1 failed (test_parse_malformed_string) same 1 failed, 58 passed
3.14 import unitxt fails 5 failed, 54 passed
3.15 import unitxt fails 5 failed, 54 passed

test_parse_malformed_string already fails on main with 3.12. The 4 other failures on 3.14/3.15 (test_parse_union_type, test_format_type_string, test_parse_type_string_with_literal, test_to_string_nested_generic_types) are in type-string parsing: on 3.14, safe_eval of union types raises KeyError: '__import__' with the empty __builtins__. That needs its own change, so I left it out of this PR.

ruff / ruff format (v0.1.6, as in pre-commit) and codespell are clean. Commits are signed off (DCO).

- Read class annotations from __annotate__ when the class namespace has no
  __annotations__ (PEP 649/749, Python 3.14+); fields were not found and
  importing unitxt failed with UnexpectedArgumentError.
- Read own annotations with inspect.get_annotations in to_dict(classes=...).
- Compare defaults with dataclasses.MISSING: dataclasses._MISSING_TYPE is
  gone in Python 3.15.
- Pass type_params to typing._eval_type on 3.13+, which 3.15 requires.

Signed-off-by: cristianchiriac <80774326+cristianchiriac@users.noreply.github.com>

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant