Skip to content

[Feature Request] Detect and warn about the TYPE_CHECKING forward reference antipattern in beartype.claw import hooks 🤮  #477

@deepyaman

Description

@deepyaman

Hello! I decided to try beartype recently (read: today). I'm working on a large codebase with a hand-rolled runtime type checking system, but everything is also type-annotated, so why not use beartype and halve the work?

Unfortunately, I ran into an issue quite quickly (may just be user error!), because of forward references with deferred imports. Here's a quick reproduction:

Module definition

bears/__init__.py

from bears.core import Bear

bears/core.py

from typing import TYPE_CHECKING, Optional

import polars as pl
from beartype import beartype

if TYPE_CHECKING:
    from bears.persistent import PolarsBear


@beartype
class Bear:
    def __init__(self, name: str, bear_type: Optional[str] = None) -> None:
        from bears.ephemeral import SpiritBear
        from bears.persistent import PolarsBear

        self.instance = (
            PolarsBear(name, pl.DataFrame())
            if bear_type == "polars"
            else SpiritBear(name)
        )

    def manifest(self) -> "PolarsBear":
        from bears.persistent import PolarsBear

        return PolarsBear(self.instance.name, pl.DataFrame())

bears/ephemeral.py

class SpiritBear:
    def __init__(self, name: str) -> None:
        self.name = name

    def __repr__(self):
        return f"SpiritBear({self.name})"

bears/persistent.py

import polars as pl


class PolarsBear:
    def __init__(self, name: str, data: pl.DataFrame) -> None:
        self.name = name
        self.data = data

    def __repr__(self):
        return f"PolarsBear({self.name}, {self.data})"

Actual error

>>> from bears import Bear
>>> bear = Bear("Berenstain")
>>> bear.manifest()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<@beartype(bears.core.Bear.manifest) at 0x105742340>", line 18, in manifest
  File "/Users/deepyaman/github/dagster-io/dagster/.venv/lib/python3.12/site-packages/beartype/_check/forward/reference/fwdrefmeta.py", line 148, in __instancecheck__
    return cls.__is_instance_beartype__(obj)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/deepyaman/github/dagster-io/dagster/.venv/lib/python3.12/site-packages/beartype/_check/forward/reference/fwdrefabc.py", line 111, in __is_instance_beartype__
    return isinstance(obj, cls.__type_beartype__)  # type: ignore[arg-type]
                           ^^^^^^^^^^^^^^^^^^^^^
  File "/Users/deepyaman/github/dagster-io/dagster/.venv/lib/python3.12/site-packages/beartype/_check/forward/reference/fwdrefmeta.py", line 304, in __type_beartype__
    referee = import_module_attr(
              ^^^^^^^^^^^^^^^^^^^
  File "/Users/deepyaman/github/dagster-io/dagster/.venv/lib/python3.12/site-packages/beartype/_util/module/utilmodimport.py", line 295, in import_module_attr
    raise exception_cls(exception_message)
beartype.roar.BeartypeCallHintForwardRefException: Forward reference "PolarsBear" unimportable from module "bears.core".
>>>

Expected output

If I comment out the @beartype decorator:

>>> from bears import Bear
>>> bear = Bear("Berenstain")
>>> bear.manifest()
PolarsBear(Berenstain, shape: (0, 0)
┌┐
╞╡
└┘)

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