{"record":{"id":"d40f35cb329e7026","repo":"roboflow/supervision","slug":"unreadable-attribute","errorCode":null,"errorMessage":"unreadable attribute","messagePattern":"unreadable attribute","errorType":"exception","errorClass":"AttributeError","httpStatus":null,"severity":"error","filePath":"src/supervision/utils/internal.py","lineNumber":166,"sourceCode":"        Args:\n            The function that is called when the property is accessed.\n        \"\"\"\n        self.fget = fget\n\n    def __get__(self, owner_self: Any, owner_cls: type | None = None) -> T:\n        \"\"\"\n        Override the __get__ method to return the result of the function call.\n\n        Args:\n            owner_self: The instance through which the attribute was accessed, or None.\n                Irrelevant for class properties.\n            owner_cls: The class through which the attribute was accessed.\n\n        Returns:\n            The result of calling the function stored in 'fget' with 'owner_cls'.\n        \"\"\"\n        if self.fget is None:\n            raise AttributeError(\"unreadable attribute\")\n        return self.fget(owner_cls)\n\n\ndef get_instance_variables(instance: Any, include_properties: bool = False) -> set[str]:\n    \"\"\"\n    Get the public variables of a class instance.\n\n    Args:\n        instance: The instance of a class\n        include_properties: Whether to include properties in the result\n\n    Usage:\n        ```pycon\n        >>> from supervision.utils.internal import get_instance_variables\n        >>> import numpy as np\n        >>> from supervision import Detections\n        >>> detections = Detections(xyxy=np.array([[1, 2, 3, 4]]))\n        >>> variables = get_instance_variables(detections)","sourceCodeStart":148,"sourceCodeEnd":184,"githubUrl":"https://github.com/roboflow/supervision/blob/7f254d9784d4c37e0f03cd89ddee164c8db099c0/src/supervision/utils/internal.py#L148-L184","documentation":"Raised by classproperty.__get__ in supervision.utils.internal when a classproperty is accessed but its fget getter is None. This mirrors Python's built-in property behavior for properties defined without a getter. In practice it signals a misconstructed classproperty, typically from subclassing or dynamically creating one without a getter function.","triggerScenarios":"Accessing SomeClass.some_classproperty where the classproperty was created with fget=None — e.g. manually assigning classproperty(None), or a decorator pipeline that dropped the getter. Reading the attribute on the class or any instance triggers it.","commonSituations":"Advanced metaprogramming that wraps or clones classproperty objects; copy-pasting the classproperty implementation and omitting the getter; trying to set a classproperty via attribute assignment (setter is not supported, leaving a broken object).","solutions":["Define the classproperty with a getter: @classproperty def name(cls): ...","If you were trying to assign to a classproperty, replace the whole object with a new classproperty(getter) instead of mutating.","Audit custom code that constructs classproperty(...) directly and ensure the callable is passed."],"exampleFix":"// before\nprop = classproperty(None)\nclass A: x = prop\nA.x  # AttributeError\n\n// after\nclass A:\n    @classproperty\n    def x(cls):\n        return 42\nA.x  # 42","handlingStrategy":"type-guard","validationCode":"if isinstance(obj, classproperty) and obj.fget is None:\n    raise AttributeError('classproperty defined without getter')","typeGuard":"def has_getter(prop: classproperty) -> bool:\n    return callable(getattr(prop, 'fget', None))","tryCatchPattern":"try:\n    value = MyClass.some_prop\nexcept AttributeError as e:\n    if 'unreadable attribute' in str(e):\n        logger.error('MyClass.some_prop is a classproperty without fget')\n    raise","preventionTips":["Always define classproperty via the @classproperty decorator with a body.","Do not assign bare classproperty objects in metaprogramming; always pass a getter."],"tags":["attributes","descriptor","metaprogramming"],"backgroundTag":null,"analyzedSha":"7f254d9784d4c37e0f03cd89ddee164c8db099c0","analyzedAt":"2026-08-15T05:13:01.950Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}