Skip to content

Test failures in Python 3.14 due to changes in metadata handling (2.0.0b7) #679

@sarahec

Description

@sarahec

Summary

Error: cls._betterproto_meta no longer exists @ betterproto/init.py:909

Reproduction Steps

While building release 2.0.0b7 for nixpkgs

Expected Results

It should build and test cleanly.

Actual Results

____________ test_message_can_instantiated[namespace_builtin_types] ____________

cls = <class 'tests.output_betterproto.namespace_builtin_types.Test'>

    @classproperty
    def _betterproto(cls: type[Self]) -> ProtoClassMetadata:  # type: ignore
        """
        Lazy initialize metadata for each protobuf class.
        It may be initialized multiple times in a multi-threaded environment,
        but that won't affect the correctness.
        """
        try:
>           return cls._betterproto_meta
                   ^^^^^^^^^^^^^^^^^^^^^
E           AttributeError: type object 'Test' has no attribute '_betterproto_meta'. Did you mean: '_betterproto'?

/nix/store/i3pvlk97zcrmgmcmrjzx1qfgkd8jhkw2-python3.14-betterproto-2.0.0b7/lib/python3.14/site-packages/betterproto/__init__.py:909: AttributeError

During handling of the above exception, another exception occurred:

test_data = TestData(plugin_module=<module 'tests.output_betterproto.namespace_builtin_types' from '/nix/var/nix/builds/nix-build-...-for-map",\n  "bool": "value-for-bool"\n}', test_name='namespace_builtin_types', file_name='namespace_builtin_types')])

    @pytest.mark.parametrize("test_data", test_cases.messages, indirect=True)
    def test_message_can_instantiated(test_data: TestData) -> None:
        plugin_module, *_ = test_data
>       plugin_module.Test()

tests/test_inputs.py:165:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
<string>:18: in __init__
    ???
/nix/store/i3pvlk97zcrmgmcmrjzx1qfgkd8jhkw2-python3.14-betterproto-2.0.0b7/lib/python3.14/site-packages/betterproto/__init__.py:755: in __post_init__
    for field_name, meta in self._betterproto.meta_by_field_name.items():
                            ^^^^^^^^^^^^^^^^^
/nix/store/i3pvlk97zcrmgmcmrjzx1qfgkd8jhkw2-python3.14-betterproto-2.0.0b7/lib/python3.14/site-packages/betterproto/__init__.py:846: in __getattribute__
    value = super().__getattribute__(name)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/nix/store/i3pvlk97zcrmgmcmrjzx1qfgkd8jhkw2-python3.14-betterproto-2.0.0b7/lib/python3.14/site-packages/betterproto/utils.py:56: in __get__
    return self.__func__(type)
           ^^^^^^^^^^^^^^^^^^^
/nix/store/i3pvlk97zcrmgmcmrjzx1qfgkd8jhkw2-python3.14-betterproto-2.0.0b7/lib/python3.14/site-packages/betterproto/__init__.py:911: in _betterproto
    cls._betterproto_meta = meta = ProtoClassMetadata(cls)
                                   ^^^^^^^^^^^^^^^^^^^^^^^
/nix/store/i3pvlk97zcrmgmcmrjzx1qfgkd8jhkw2-python3.14-betterproto-2.0.0b7/lib/python3.14/site-packages/betterproto/__init__.py:691: in __init__
    self.default_gen = self._get_default_gen(cls, fields)
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/nix/store/i3pvlk97zcrmgmcmrjzx1qfgkd8jhkw2-python3.14-betterproto-2.0.0b7/lib/python3.14/site-packages/betterproto/__init__.py:698: in _get_default_gen
    return {field.name: cls._get_field_default_gen(field) for field in fields}
                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

cls = <class 'tests.output_betterproto.namespace_builtin_types.Test'>
field = Field(name='int',type=Field(name='str',type=<class 'str'>,default=<object object at 0x10428ecd0>,default_factory=<data...oto_type='string', map_types=None, group=None, wraps=None, optional=False)}),kw_only=
False,doc=None,_field_type=_FIELD)

    @classmethod
    def _get_field_default_gen(cls, field: dataclasses.Field) -> Any:
        t = cls._type_hint(field.name)

        is_310_union = isinstance(t, _types_UnionType)
        if hasattr(t, "__origin__") or is_310_union:
            if is_310_union or t.__origin__ is Union:
                # This is an optional field (either wrapped, or using proto3
                # field presence). For setting the default we really don't care
                # what kind of field it is.
                return type(None)
            if t.__origin__ is list:
                # This is some kind of list (repeated) field.
                return list
            if t.__origin__ is dict:
                # This is some kind of map (dict in Python).
                return dict
            return t
>       if issubclass(t, Enum):
           ^^^^^^^^^^^^^^^^^^^
E       TypeError: issubclass() arg 1 must be a class

/nix/store/i3pvlk97zcrmgmcmrjzx1qfgkd8jhkw2-python3.14-betterproto-2.0.0b7/lib/python3.14/site-packages/betterproto/__init__.py:1191: TypeError.
.
.
=========================== short test summary info ============================
FAILED tests/test_inputs.py::test_message_can_instantiated[namespace_builtin_types] - TypeError: issubclass() arg 1 must be a class
FAILED tests/test_inputs.py::test_message_equality[namespace_builtin_types] - TypeError: issubclass() arg 1 must be a class
FAILED tests/test_inputs.py::test_message_json[namespace_builtin_types] - TypeError: issubclass() arg 1 must be a class

### System Information

Python 3.14.2


### Checklist

- [x] I have searched the issues for duplicates.
- [x] I have shown the entire traceback, if possible.
- [x] I have verified this issue occurs on the latest prelease of betterproto which can be installed using `pip install -U --pre betterproto`, if possible.

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions