Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve system check registry hints #1249

Merged
merged 7 commits into from
Jan 8, 2023
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 16 additions & 12 deletions django-stubs/core/checks/registry.pyi
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
from collections.abc import Callable, Sequence
from typing import Any, Protocol, TypeVar
from collections.abc import Callable, Iterable, Sequence
from typing import Any, Protocol, TypeVar, overload

from django.apps.config import AppConfig
from django.apps.registry import Apps
from django.core.checks.messages import CheckMessage
from typing_extensions import TypeAlias

class Tags:
admin: str
Expand All @@ -19,7 +19,10 @@ class Tags:
translation: str
urls: str

_CheckCallable: TypeAlias = Callable[..., Sequence[CheckMessage]]
class _CheckCallable(Protocol):
def __call__(
self, *, app_configs: Apps, databases: Sequence[str] | None, **kwargs: Any
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

app_configs has the wrong type - it's a list of AppConfig, or None.

See https://docs.djangoproject.com/en/4.1/topics/checks/#writing-your-own-checks

The check function must accept an app_configs argument; this argument is the list of applications that should be inspected. If None, the check must be run on all installed apps in the project.

And an example check function in one of my packages: https://github.com/adamchainz/django-linear-migrations/blob/main/src/django_linear_migrations/apps.py#L129

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doh that was sloppy. Thanks for the fixes!

) -> Iterable[CheckMessage]: ...

_C = TypeVar("_C", bound=_CheckCallable)

Expand All @@ -31,21 +34,22 @@ class CheckRegistry:
registered_checks: set[_ProcessedCheckCallable]
deployment_checks: set[_ProcessedCheckCallable]
def __init__(self) -> None: ...
def register(
self, check: _CheckCallable | str | None = ..., *tags: str, **kwargs: Any
) -> Callable[[_CheckCallable], _ProcessedCheckCallable] | _ProcessedCheckCallable: ...
@overload
def register(self, __check: _C) -> _ProcessedCheckCallable[_C]: ...
@overload
def register(self, *tags: str, **kwargs: Any) -> Callable[[_C], _ProcessedCheckCallable[_C]]: ...
def run_checks(
self,
app_configs: Sequence[AppConfig] | None = ...,
tags: Sequence[str] | None = ...,
include_deployment_checks: bool = ...,
databases: Any | None = ...,
databases: Sequence[str] | None = ...,
) -> list[CheckMessage]: ...
def tag_exists(self, tag: str, include_deployment_checks: bool = ...) -> bool: ...
def tags_available(self, deployment_checks: bool = ...) -> set[str]: ...
def get_checks(self, include_deployment_checks: bool = ...) -> list[_ProcessedCheckCallable]: ...

registry: CheckRegistry
register: Any
run_checks: Any
tag_exists: Any
registry: CheckRegistry = ...
register = registry.register
run_checks = registry.run_checks
tag_exists = registry.tag_exists
24 changes: 24 additions & 0 deletions tests/typecheck/core/test_checks.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
- case: test_checks_register
main: |
from typing import Any, Sequence, Optional

from django.core.checks import register, Warning, CheckMessage
from django.apps.registry import Apps


@register("foo", deploy=True)
def check_foo(app_configs: Apps, databases: Optional[Sequence[str]], **kwargs: Any) -> list[Warning]:
if databases and 'databass' in databases:
return [Warning("Naughty list")]
return []
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

most checks don't take a databases argument, so I'd like a test that checks that situation too


reveal_type(check_foo) # N: Revealed type is "django.core.checks.registry._ProcessedCheckCallable[def (app_configs: django.apps.registry.Apps, databases: Union[typing.Sequence[builtins.str], None], **kwargs: Any) -> builtins.list[django.core.checks.messages.Warning]]"
reveal_type(check_foo.tags) # N: Revealed type is "typing.Sequence[builtins.str]"

@register
def check_bar(**kwargs: Any) -> Sequence[CheckMessage]: ...

reveal_type(check_bar) # N: Revealed type is "django.core.checks.registry._ProcessedCheckCallable[def (**kwargs: Any) -> typing.Sequence[django.core.checks.messages.CheckMessage]]"

@register() # E: Value of type variable "_C" of function cannot be "Callable[[int], Sequence[CheckMessage]]"
def wrong_args(bla: int) -> Sequence[CheckMessage]: ...