Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Nov 14, 2025

Motivation

Python 3.14 with PEP 649 allows __annotate_func__ to be None when classes have no annotations or are created dynamically. The Collection metaclass assumed it was always callable, causing TypeError: 'NoneType' object is not callable during module import in certain scenarios.

Changes

  • dataframely/collection/_base.py: Added None and callable checks before invoking __annotate_func__, falling back to empty annotations dict
  • tests/collection/test_base.py: Added Python 3.14-specific test for __annotate_func__ = None handling

The fix handles edge cases like:

# Dynamic class creation with __annotate_func__ = None
namespace = {
    '__annotate_func__': None,
}
TestCollection = CollectionMeta('TestCollection', (Collection,), namespace)
# Previously: TypeError - Now: works correctly with no members
Original prompt

This section details on the original issue you should resolve

<issue_title>Python 3.14 and Dataframely v2 incompatibility in certain scenarios</issue_title>
<issue_description>We have a Python 3.14 codebase and trying to migrate to Dataframely v2. In many scenarios we encounter exceptions during module import, which appear to be related to the introduction in 3.14 of deferred evaluation of annotations (https://docs.python.org/3/reference/compound_stmts.html#annotations). Notably, things work fine with Dataframely v1.

The stack trace is as follows:

Traceback (most recent call last):
  File "xxxx.py", line 8, in <module>
    import dataframely as dy
  File "/xx/.venv/lib/python3.14/site-packages/dataframely/__init__.py", line 17, in <module>
    from .collection import (
    ...<4 lines>...
    )
  File "/xx/.venv/lib/python3.14/site-packages/dataframely/collection/__init__.py", line 5, in <module>
    from .collection import (
    ...<4 lines>...
    )
  File "/xx/.venv/lib/python3.14/site-packages/dataframely/collection/collection.py", line 52, in <module>
    class Collection(BaseCollection, ABC):
    ...<1159 lines>...
                )
  File "/xx/.venv/lib/python3.14/site-packages/dataframely/collection/_base.py", line 115, in __new__
    result.update(mcs._get_metadata_recursively(base))
                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
  File "/xx/.venv/lib/python3.14/site-packages/dataframely/collection/_base.py", line 187, in _get_metadata_recursively
    result.update(CollectionMeta._get_metadata(kls.__dict__))  # type: ignore
                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^
  File "/xx/.venv/lib/python3.14/site-packages/dataframely/collection/_base.py", line 200, in _get_metadata
    annotations = source["__annotate_func__"](Format.VALUE)
TypeError: 'NoneType' object is not callable

Some thoughts from Claude:

The core issue is that dataframely assumes __annotate_func__ is always callable when it exists, but in Python 3.14 with PEP 649, it can be None when:

  • A class has no annotations
  • Annotations are being processed during certain import contexts
  • The annotation evaluation is deferred and not yet available

The fix is simple: check if __annotate_func__ is not None (and ideally also callable) before calling it, and fall back to using __annotations__ directly when it's not available.

This is a common compatibility issue that many metaclass-based libraries are encountering with Python 3.14's new annotation system.

Thanks in advance for taking a look!</issue_description>

Comments on the Issue (you are @copilot in this section)

@borchero Ha, thanks a lot for raising this! When adding Python 3.14 to the CI matrix, we realized that there were quite a few issues and using `__annotate_func__` fixed them. At the time, I was, however, already not 100% certain that this is the way to do it. I think I'd need to understand the behavior of Python 3.14 a little better.

Since we do not encounter any issues with Python 3.14 in our CI, could you share an MWE to allow us to reproduce your issue?</comment_new>


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

Co-authored-by: borchero <22455425+borchero@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix Python 3.14 and Dataframely v2 incompatibility issues Fix Python 3.14 __annotate_func__ None handling in Collection metaclass Nov 14, 2025
Copilot AI requested a review from borchero November 14, 2025 20:27
Copilot finished work on behalf of borchero November 14, 2025 20:27
@github-actions
Copy link

Thank you for opening this pull request! 👋🏼

This repository requires pull request titles to follow the Conventional Commits specification and it looks like your proposed title needs to be adjusted.

Details
No release type found in pull request title "Fix Python 3.14 `__annotate_func__` None handling in Collection metaclass". Add a prefix to indicate what kind of release this pull request corresponds to. For reference, see https://www.conventionalcommits.org/

Available types:
 - feat: A new feature
 - fix: A bug fix
 - docs: Documentation only changes
 - style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
 - refactor: A code change that neither fixes a bug nor adds a feature
 - perf: A code change that improves performance
 - test: Adding missing tests or correcting existing tests
 - build: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)
 - ci: Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs)
 - chore: Other changes that don't modify src or test files
 - revert: Reverts a previous commit

@codecov
Copy link

codecov bot commented Nov 14, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (1660d88) to head (2eaef77).

Additional details and impacted files
@@            Coverage Diff            @@
##              main      #227   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           53        53           
  Lines         3019      3021    +2     
=========================================
+ Hits          3019      3021    +2     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

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.

Python 3.14 and Dataframely v2 incompatibility in certain scenarios

2 participants