Skip to content

Instance attribute becomes Unknown when type has __getattr__, but pyright handles it correctly #1719

@janfrederik

Description

@janfrederik

Description

When a class declares an instance attribute with an explicit type annotation, and that type is a class with __getattr__, basedpyright incorrectly reports the attribute as Unknown when accessed. Standard pyright does not have this issue.

The type is explicitly declared, and the assignment is type-checked. There's no code path where the attribute could be anything other than the declared type.

Reproduction

from __future__ import annotations
from typing import TYPE_CHECKING, Any

if TYPE_CHECKING:
    from gitlab.v4.objects import Project  # Has __getattr__ via RESTObject base class

class Handler:
    _project: Project  # Explicitly declared type

    def __init__(self, project: Project) -> None:
        self._project = project  # Assignment is type-checked

    def get_project(self) -> Project:
        return self._project  # Warning: Return type is unknown

Playground link - uses a simplified example since gitlab isn't available in playground.

Expected Behavior

self._project should have type Project because:

  1. The attribute is explicitly declared as _project: Project
  2. The assignment self._project = project is type-checked to ensure type safety
  3. There's no code path where _project could be anything other than Project

The __getattr__ on Project is irrelevant - it affects attribute access on Project instances, not how other classes store Project as their own attribute.

Actual Behavior

basedpyright reports:

warning: Return type is unknown (reportUnknownVariableType)

Comparison with pyright

Standard pyright (tested with pyright 1.1.x via uv run --with pyright pyright) reports no warnings for the same code.

Environment

  • basedpyright 1.37.2
  • Python 3.14
  • The issue occurs with python-gitlab's Project class, which inherits from RESTObject that has __getattr__(self, name: str) -> Any

Workaround

Using __slots__ on the holder class makes basedpyright correctly recognize the type:

class Handler:
    __slots__ = ("_project",)
    _project: Project
    # ... now _project is correctly typed as Project

But this shouldn't be necessary - the explicit type declaration should be sufficient.

Metadata

Metadata

Assignees

No one assigned

    Labels

    awaiting responsewaiting for more info from the author - will eventually close if they don't respond

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions