Nearly all my Wagtail models files are full of errors according to Pylance and I'm not sure how to silence them without either adding # type: ignore
to hundreds of lines or turning off Pylance rules that help catch genuine bugs. The errors often come from RichTextField
properties on my models. Here is a simple example:
from wagtail.models import Page
from wagtail.fields import RichTextField
class SpecialPage(Page):
introduction = RichTextField(
blank=True,
features=settings.RICHTEXT_ADVANCED,
help_text="Text that appears at the top of the page.",
)
where RICHTEXT_ADVANCED
is a list[str]
in my settings file. This code works fine. The arguments passed to RichTextField
all exist in its __init__
method or the __init__
method of one a parent class. Yet Pylance in VS Code underlines all three lines of introduction
in red and says:
No overloads for "__new__" match the provided arguments Argument types: (Literal[True], Any, Literal['Text that appears at the top of the page.']) Pylance(reportCallIssue)
Is this a bug in Pylance? Is there a way around it other than the two (undesirable) approaches I mentioned above? Or could Wagtail provide more explicit type hints or @overload
indicators to prevent the errors?
The class inheritance goes RegisterLookupMixin > Field (has blank
and help_text
) > TextField (has features
) > RichTextField. None of these classes have a __new__
method. The values I'm providing all match the types defined in the parent classes. I'm on a 5.x Wagtail, has this perhaps been fixed in more recent releases?
You're hitting a deficiency in pylance: the heuristic doesn't apply in your case and causes troubles.
I don't have anything powered by pylance available to investigate this, but you may try with a smaller snippet to check if pylance thinks that def __init__(*args, **kwargs)
on a subclass means "use same arguments as parent". This is often true, but also often wrong.
class A:
def __init__(self, foo):
self.foo = foo
class B(A):
def __init__(self, *args, **kwargs):
self.bar = kwargs.pop("bar")
super().__init__(*args, **kwargs)
B(foo=0, bar=1)
Pyright accepts this code, so the problem is most likely in its wrapper - pylance.
Neither Wagtail nor Django are type hinted, so this example is representative of what you observe. RichTextField
defines a catch-all args, kwargs constructor, so pyright looks further up the inheritance chain. All the way to django.db.models.TextField
with def __init__(self, *args, db_collation=None, **kwargs)
and finally up to plain Field
that defines all arguments explicitly here.
Now, this should be possible to circumvent somehow, right? Right?..
useLibraryCodeForTypes
to false in your pyright configuration - that may work. It won't help on my example, though, as all code there is inline. This will make your type checking less reliable, but also hepl you avoid spurious errors from unexpected inference of overly strict types.django.db.models.Field
, add arguments supported by RichTextField
to that list and write your wrapper class (and use it everywhere instead of RichTextField
). To avoid harming your runtime code, here's what it may look like:from typing import TYPE_CHECKING
from django.db.models.fields import NOT_PROVIDED
from wagtail.fields import RichTextField
class MyRichTextField(RichTextField):
if TYPE_CHECKING: # False at runtime
def __init__(
self,
verbose_name=None,
name=None,
primary_key=False,
max_length=None,
unique=False,
blank=False,
null=False,
db_index=False,
rel=None,
default=NOT_PROVIDED,
editable=True,
serialize=True,
unique_for_date=None,
unique_for_month=None,
unique_for_year=None,
choices=None,
help_text="",
db_column=None,
db_tablespace=None,
auto_created=False,
validators=(),
error_messages=None,
db_comment=None,
db_default=NOT_PROVIDED,
*,
# from TextField
db_collation=None,
# from RichTextField
editor="default",
features=None,
): ...
You may add annotations to the fields you're going to use, if you'd like to. If you want, django-stubs
already define type hints for these arguments, right here - you can use that for reference.