django-stubs
django-stubs copied to clipboard
BaseFormSet - TypeError: 'type' object is not subscriptable
Bug report
What's wrong
Having recently updated mypy and django-stubs, the following error started occurring:
error: Missing type parameters for generic type "BaseFormSet" [type-arg]
This was pointing at my formset class, so I added the generic type and, as an example, had this:
class MyForm(forms.Form):
...
class MyFormset(forms.BaseFormSet[MyForm]):
...
Which appeased the type checker, but unfortunately resulted in the following runtime error:
File ".../forms.py", line ..., in <module>
class MyFormset(forms.BaseFormSet[MyForm]):
TypeError: 'type' object is not subscriptable
This runtime error occurs even with the django_stubs_ext helper installed, as well as when using a string:
class MyFormset(forms.BaseFormSet["MyForm"]):
...
Note, this is running mypy with --strict, which enables --disallow-any-generics.
How is that should be
After adding the generic type to the formset class the type checker should pass and no runtime error should occur.
System information
- OS: macOS 12.4
pythonversion: 3.10.4djangoversion: 4.0.4mypyversion: 0.950django-stubsversion: 1.11.0django-stubs-extversion: 0.4.0
It doesn't reproduce with master.
@sobolevn Do we need a minor/patch release for django_stubs_ext or is there something to wait for? This was added in #909 together with making BaseFormSet generic in stubs. There was a release of main package since that.
adding from __future__ import annotations fixed it for me.
Try [email protected]. Does it work for you?
If not, PRs are welcome.
The original mypy error hasn't gone away (baseclass isn't a valid type), this is a test to reproduce the problem I'm dealing with, if you want, I can make a PR for it but I don't know if this is something that's even supposed to be possible or not:
- case: inlineformset_factory_extended
main: |
from typing import Any, Type
from django import forms
from myapp.models import Article, Category
ArticleFS: Type[forms.BaseInlineFormSet[Article, Category, Any]] = forms.inlineformset_factory(Category, Article)
ArticleFS(instance=Article()) # E: Argument "instance" to "BaseInlineFormSet" has incompatible type "Article"; expected "Optional[Category]"
class CustomArticleFS(ArticleFS): # It will throw a "ArticleFS" is not a valid type error here.
def __init__(self, *args, **kwargs):
print('hi')
super().__init__(*args, **kwargs)
fs = CustomArticleFS(instance=Category())
reveal_type(fs.instance) # N: Revealed type is "myapp.models.Category"
installed_apps:
- myapp
files:
- path: myapp/__init__.py
- path: myapp/models.py
content: |
from django.db import models
class Article(models.Model):
pass
class Category(models.Model):
pass
It is a completely unrelated issue. The initial problem was with generics: BaseFormSet got type arguments after django-stubs upgrade, but it was not reflected in django_stubs_ext PyPI version.
What you're facing now is a known mypy limitation: mypy does not support dynamic base classes (see this wontfix issue). django-stubs has nothing to do with it.
Another option is to declare your formset class ArticleFS(forms.BaseInlineFormSet[...]) first and then pass formset=ArticleFS to inlineformset_factory. It is not supported (return type will be BaseInlineFormSet anyway), but in this case a simple cast will be sufficient. I have one simple solution for this (overload to return type of formset argument if it is given), but it is not 100% safe and may look odd.
So, it would be great if you can open another issue targeting this problem (inlineformset_factory + custom formset class) to make it a) searchable for future readers and b) usable to discuss this problem. This issue seems resolved.