Skip to content

Self used in contravariant positions #2605

Open
@jorenham

Description

@jorenham

In e.g. django.db.models.Model.[a]refresh_from_db, Self is used in a contravariant position:

def refresh_from_db(
self,
using: str | None = None,
fields: Iterable[str] | None = None,
from_queryset: QuerySet[Self] | None = None,
) -> None: ...
async def arefresh_from_db(
self,
using: str | None = None,
fields: Iterable[str] | None = None,
from_queryset: QuerySet[Self] | None = None,
) -> None: ...

When I override this in my own model as e.g.

    @override
    def refresh_from_db(
        self,
        using: str | None = None,
        fields: Iterable[str] | None = None,
        from_queryset: QuerySet[Self] | None = None,
    ) -> None:

it violates the LSP, as explained in https://discuss.python.org/t/unsoundness-of-contravariant-self-type/86338. On pyright>=1.1.399, it will reportIncompatibleMethodOverride.

The workaround is to instead use QuerySet[Any] in the code above.

Perhaps a self: T with QuerySet[T] could work here? I'm not sure whether that'd make it theoretically sound, but it might be worth a shot 🤷🏻.

System information

  • OS: irrelevant
  • python version: 3.13
  • django version: 5.2
  • mypy version: pyright==1.1.399 or basedpyright==1.28.5
  • django-stubs version: 5.1.3
  • django-stubs-ext version: 5.1.3

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions