最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

python - Advice on using Wagtail (e.g. RichTextField) with Pylance type checking - Stack Overflow

programmeradmin4浏览0评论

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?

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?

Share Improve this question edited Mar 19 at 22:13 InSync 11.1k4 gold badges18 silver badges56 bronze badges asked Mar 19 at 19:46 phette23phette23 5254 silver badges14 bronze badges 9
  • 1 Definitely not normal behaviour, even for 5.x. Have you tried eliminating parameters? What do you see if you just use introduction = RichTextField(). If you create a new project and add this page class definition, do you see the same error? – Rich - enzedonline Commented Mar 19 at 20:40
  • @Rich-enzedonline thanks this helps narrow it down to something about features. If I delete parameter, predictably it's fine. If I start a fresh site on the same version, RichTextField(blank=True, help_text="") is also OK. I see errors as soon as I add a features parameter e.g. body = RichTextField(blank=True, features=['p'], help_text="Content"). But obviously I don't want to delete the features lists from all of my rich text fields. – phette23 Commented Mar 19 at 22:17
  • If this code works fine at runtime, then it means that your django typing information doesn't match your runtime version of django. django.db.models.fields.Field does not currently have a keyword parameter named features, and the typing stubs reflects this. – dROOOze Commented Mar 19 at 23:11
  • @dROOOze RichTextField comes from Wagtail, not from django internals. And wagtail isn't typed, and there are no stubs for it. So yes, this seems to be a consequence of pyright non-conformance - it attempts to analyze untyped functions and fails to do so correctly, probably assuming that *args, **kwargs arguments to __init__ are same as parent class' args and kwargs? – STerliakov Commented Mar 20 at 0:29
  • @STerliakov pyright's default behaviour doesn't care about whether a library is typed; it'll follow them regardless (based on its useLibraryCodeForTypes option), so wagtail.fields.RichTextField just inherits the class constructor from the django class. – dROOOze Commented Mar 20 at 0:37
 |  Show 4 more comments

1 Answer 1

Reset to default 1

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?..

  • Try setting 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.
  • You can copy all arguments from 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.

发布评论

评论列表(0)

  1. 暂无评论