Skip to content

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

Closed as not planned

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.

Activity

  1. DetachHead commented on Feb 7, 2026

    @DetachHead
    Owner

    Standard pyright does not have this issue.

    this is just because pyright's defaults are less strict than basedpyright's (more info). the same error occurs in pyright if you enable these rules:

    [tool.pyright]
    reportUnknownMemberType = true
    reportUnknownVariableType = true

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

    not sure what's going on here... i assume this isn't what you were trying to show:

    Image

    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

    i'm not sure what was supposed to be in the minimal playground example, but in this example here the issue doesn't seem to be related to the __getattr__, but rather the RepositoryMixin class that Project extends:

    # gitlab/v4/objects/repositories.py
    if TYPE_CHECKING:
        # When running mypy we use these as the base classes
        _RestObjectBase = gitlab.base.RESTObject
    else:
        _RestObjectBase = object
    
    
    class RepositoryMixin(_RestObjectBase):
        ...

    it looks like some sort of hack to work around a mypy bug. but this isn't compatible with pyright because the base module cannot be accessed directly from gitlab that way (more info). this causes the _RestObjectBase class to be Unknown, which is whatt's causing your error.

    i would recommend opening an issue at https://github.com/python-gitlab/python-gitlab.

    here's a minimal repro:

    class Foo(asdf):...  # error: reportUndefinedVariable
    
    class Handler:
        _project: Foo
    
        def __init__(self, project: Foo) -> None:
            self._project = project
    
        def get_project(self):
            return self._project  # Warning: Return type is unknown
  2. added
    awaiting responsewaiting for more info from the author - will eventually close if they don't respond
    and removed
    needs investigationawaiting verification by a maintainer that the issue is valid
    on Feb 7, 2026
  3. janfrederik commented on Feb 10, 2026

    @janfrederik
    Author

    @DetachHead Ok, clear. Thank you for your analysis!

    So I see a that just adding a simple import gitlab.base to the type checking block in gitlab/v4/objects/repositories.py does the trick:

    # gitlab/v4/objects/repositories.py
    if TYPE_CHECKING:
        # When running mypy we use these as the base classes
        import gitlab.base         # Add this line
        _RestObjectBase = gitlab.base.RESTObject
    else:
        _RestObjectBase = object
    
    
    class RepositoryMixin(_RestObjectBase):
        ...

    I'll file an issue with python-gitlab.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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