facebook / facebook/pyrefly

invalid-inheritance on a call-expression base class even when the callable returns type[...] (mixin factory)

Open
#3,931 4 comments 1 reaction 1 assignee Claimed by @asukaminato0721 View on GitHub
django
Dominant language
Rust
Stars
7k
Forks
516
PR merge metrics
No merged PRs in 30d

Description

### Summary

Pyrefly reports `invalid-inheritance` ("Invalid expression form for base class") for a base class that is a **call expression**, even when the callable is annotated to return a class type (`type[...]`). This "mixin factory" pattern is valid Python and is accepted by both mypy and pyright.

### Repro

`pyrefly.toml`:
```toml
project-includes = ["m.py"]
```

`m.py`:
```python
class Mixin: ...
def factory(name: str) -> type[Mixin]: ...

class C(factory("x")): # <-- flagged
pass
```

`pyrefly check` (v1.0.0):
```
ERROR Invalid expression form for base class: `factory("x")` [invalid-inheritance]
```

### Expected

No error. `factory("x")` has type `type[Mixin]`, which is a valid base class. mypy and pyright both accept this.

### Real-world motivation

`django_prometheus`'s `ExportModelOperationsMixin(model_name)` returns a dynamically-built mixin class, used as:
```python
class User(ExportModelOperationsMixin("user"), AbstractUser): ...
```
Every model using this factory gets a spurious `invalid-inheritance`. (django_prometheus is untyped so the call returns `Any` in that case — but as the repro above shows, the error fires even when the factory is fully typed to return `type[Mixin]`.)

### Note

The same `m.py` checked with **no config** (the `basic` preset) reports **0 errors** — the error only appears under a project config. So pyrefly can evidently accept the form in one mode but not the other.

### Environment
- pyrefly 1.0.0 (also reproduced on 1.1.1)
- Python 3.12

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.