djangorestframework-stubs icon indicating copy to clipboard operation
djangorestframework-stubs copied to clipboard

format_suffix_patterns should have covariant type for urlpatterns parameter

Open peterschutt opened this issue 3 years ago • 4 comments

Version 1.4.0 of DRF-stubs and Following along with the DRF tutorial I've got the following pattern (mypy output inline in comments):

urlpatterns = [
    path("tree/", views.NodeList.as_view()),
    path("tree/<int:pk>", views.NodeDetail.as_view()),
]

# reveal_type(urlpatterns)  # Revealed type is "builtins.list[django.urls.resolvers.URLPattern*]"

urlpatterns = format_suffix_patterns(urlpatterns)  

#  Argument 1 to "format_suffix_patterns" has incompatible type "List[URLPattern]"; expected "List[Union[URLResolver, RoutePattern, URLPattern, Pattern[Any]]]"

Looking at the source for format_suffix_patterns() and apply_suffix_patterns() they only iterate over the contents of the urlpatterns input, nothing specific to it being a list.

Can we change the urlpatterns parameter from List to Iterable?

from typing import Iterable

def apply_suffix_patterns(
    urlpatterns: Iterable[Union[URLResolver, RoutePattern, URLPattern, Pattern]],
    suffix_pattern: Union[str, Pattern],
    suffix_required: bool,
    suffix_route: Optional[str] = ...,
) -> List[URLPattern]: ...
def format_suffix_patterns(
    urlpatterns: Iterable[Union[URLResolver, RoutePattern, URLPattern, Pattern]],
    suffix_required: bool = ...,
    allowed: Optional[List[Union[URLPattern, Pattern, str]]] = ...,
) -> List[URLPattern]: ...

System information

  • OS: Debian GNU/Linux 11 (bullseye)
  • python version: 3.10.1
  • django version: 4.0.1
  • mypy version: 0.931
  • django-stubs version: 0.3.1

peterschutt avatar Jan 23 '22 06:01 peterschutt

PRs are welcome!

sobolevn avatar Jan 23 '22 07:01 sobolevn

I guess the same is happening when you try to add routers.ursl to urlpatterns:

from django.urls import path
from rest_framework.routers import DefaultRouter

urlpatterns = [
    path(route="one/", view=OneView.as_view(), name="some-view"),
    ...
]
router = DefaultRouter()
router.register(prefix="two", viewset=views.TwoViewSet, basename="two")

urlpatterns += router.urls


Argument 1 to "__iadd__" of "list" has incompatible type "List[Union[URLPattern, URLResolver]]"; expected "Iterable[URLPattern]"  [arg-type]

Or it is completely different issue?

sshishov avatar Jun 02 '22 14:06 sshishov

Different issue.

Mypy infers the type of your local variable urlpatterns from this line:

urlpatterns = [
    path(route="one/", view=OneView.as_view(), name="some-view"),
    ...
]

It infers it as list[URLPattern].

Give it a type when you declare it that supports URLResolver as well:

urlpatterns: list[URLPattern, URLResolver] = [
    path(route="one/", view=OneView.as_view(), name="some-view"),
    ...
]

peterschutt avatar Jun 02 '22 21:06 peterschutt