_bind_property_to raises TypeError when the reference field is inherited from a base class

Open
#39 0 comments 0 reactions 1 assignee View on GitHub

@plamen-neykov is already working on this.

Since Sep 16, 2026.

Assessment

This issue has not been assessed yet.

Description

bug

Bug Report

_bind_property_to raises TypeError when the reference field is inherited from a base class
Steps to Reproduce
  1. Clone the repository and set up the development environment:
git clone https://github.com/finos/rune-python-runtime
cd rune-python-runtime
./dev_clean_setup.sh
source .pydevenv/bin/activate
  1. Run the following self-contained script (no external dependencies beyond the runtime itself):
import json
from typing_extensions import Annotated
from pydantic import Field
from rune.runtime.base_data_class import BaseDataClass

# A keyed type — instances can be registered under an external key
class Location(BaseDataClass):
    _ALLOWED_METADATA = {'@key:external'}
    name: str = Field(..., description='location name')

# Base class declares the reference field
class EntityBase(BaseDataClass):
    location: Annotated[
        Location,
        Location.serializer(),
        Location.validator(allowed_meta=('@ref:external',))
    ] = Field(None, description='reference to location')
    _KEY_REF_CONSTRAINTS = {'location': {'@ref:external'}}

# Subclass inherits 'location' — it is NOT in Entity.__annotations__
class Entity(EntityBase):
    label: str = Field(..., description='entity label')

# Container holds both the key targets and the referencing objects
class Container(BaseDataClass):
    locations: list[Location] = Field(..., description='locations')
    entities: list[Entity] = Field(..., description='entities')

data = json.dumps({
    "locations": [
        {"@key:external": "loc1", "name": "New York"}
    ],
    "entities": [
        {"label": "Desk A", "location": {"@ref:external": "loc1"}}
    ]
})

c = Container.rune_deserialize(data, validate_model=False)
Expected Result

Deserialization completes and c.entities[0].location is a resolved Location instance.

Actual Result

resolve_references raises TypeError:

  File ".../rune/runtime/base_data_class.py:343: in resolve_references
    self._bind_property_to(prop_nm, ref)
  File ".../rune/runtime/metadata.py:304: in _bind_property_to
    or isinstance(ref.target, allowed_type)):
TypeError: isinstance() arg 2 must be a type, a tuple of types, or a union
Root Cause

_bind_property_to in metadata.py looks up the field's type annotation using self.__class__.__annotations__:

field_type = self.__class__.__annotations__.get(property_nm)   # line 301
allowed_type = _get_basic_type(field_type)
if not (isinstance(allowed_type, str)
        or isinstance(ref.target, allowed_type)):              # line 303-304

Python's __annotations__ only contains annotations declared directly on the class — not those inherited from a base class. When property_nm is defined on a parent class, .get() returns None. _get_basic_type(None) also returns None, and isinstance(ref.target, None) raises TypeError.

Environment
  • rune-python-runtime version: current main
  • Python: 3.12+
  • OS: macOS Darwin 25.6.0
Additional Context

Recommended fix — guard against None before the isinstance call. A missing annotation means the field type cannot be determined (it is inherited), so the type check should be skipped:

field_type = self.__class__.__annotations__.get(property_nm)
allowed_type = _get_basic_type(field_type)
if allowed_type is not None and not (isinstance(allowed_type, str)
        or isinstance(ref.target, allowed_type)):
    raise ValueError("Can't set reference. Incompatible types: "
                     f"expected {allowed_type}, "
                     f"got {ref.target.__class__}")

A more complete fix would replace self.__class__.__annotations__ with typing.get_type_hints(self.__class__), which traverses the MRO and resolves forward references. However, get_type_hints() can raise when forward references cannot be resolved in the current scope, so the None-guard is the safer immediate change.

Dominant language
Python
Stars
0
Forks
3
Avg merge
5m
Merged PRs (30d)
1

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from finos/rune-python-runtime

All issues in finos/rune-python-runtime

Similar issues

More Python issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.