From b397ec04a95e009c7f86b6bcb7325d2059384dc1 Mon Sep 17 00:00:00 2001 From: Maxim Kurnikov Date: Wed, 10 Jul 2019 18:19:37 +0300 Subject: [PATCH] add utils_tests test folder to typechecking (#106) --- django-stubs/template/base.pyi | 4 +- django-stubs/test/utils.pyi | 10 ++--- django-stubs/utils/autoreload.pyi | 49 ++++++++++++++++++++++++- django-stubs/utils/datastructures.pyi | 3 +- django-stubs/utils/dateformat.pyi | 4 +- django-stubs/utils/decorators.pyi | 4 +- django-stubs/utils/hashable.pyi | 3 ++ django-stubs/utils/http.pyi | 16 ++++---- django-stubs/utils/inspect.pyi | 4 +- django-stubs/utils/timezone.pyi | 15 +++++--- django-stubs/utils/topological_sort.pyi | 6 +++ django-stubs/utils/tree.pyi | 4 +- scripts/typecheck_tests.py | 26 ++++++++++--- 13 files changed, 114 insertions(+), 34 deletions(-) create mode 100644 django-stubs/utils/hashable.pyi create mode 100644 django-stubs/utils/topological_sort.pyi diff --git a/django-stubs/template/base.pyi b/django-stubs/template/base.pyi index ce05838..a6015e4 100644 --- a/django-stubs/template/base.pyi +++ b/django-stubs/template/base.pyi @@ -59,7 +59,9 @@ class Template: engine: Optional[Engine] = ..., ) -> None: ... def __iter__(self) -> None: ... - def render(self, context: Union[Context, Dict[str, Any]], request: Optional[HttpRequest] = ...) -> Any: ... + def render( + self, context: Optional[Union[Context, Dict[str, Any]]] = ..., request: Optional[HttpRequest] = ... + ) -> Any: ... def compile_nodelist(self) -> NodeList: ... def get_exception_info(self, exception: Exception, token: Token) -> Dict[str, Any]: ... diff --git a/django-stubs/test/utils.pyi b/django-stubs/test/utils.pyi index 35e96c1..af1776e 100644 --- a/django-stubs/test/utils.pyi +++ b/django-stubs/test/utils.pyi @@ -1,24 +1,24 @@ import decimal import warnings -from io import StringIO from contextlib import contextmanager from decimal import Decimal -from typing import Any, Callable, Dict, Iterator, List, Optional, Set, Tuple, Type, Union, IO, Iterable, Mapping +from io import StringIO +from typing import Any, Callable, Dict, Iterable, Iterator, List, Mapping, Optional, Set, Tuple, Type, Union from django.apps.registry import Apps from django.core.checks.registry import CheckRegistry from django.db.models.lookups import Lookup, Transform +from django.db.models.query_utils import RegisterLookupMixin from django.test.runner import DiscoverRunner from django.test.testcases import SimpleTestCase from django.conf import LazySettings, Settings -from django.db.models.query_utils import RegisterLookupMixin - -from django.db.models.fields import Field _TestClass = Type[SimpleTestCase] _DecoratedTest = Union[Callable, _TestClass] +TZ_SUPPORT: bool = ... + class Approximate: val: Union[decimal.Decimal, float] = ... places: int = ... diff --git a/django-stubs/utils/autoreload.pyi b/django-stubs/utils/autoreload.pyi index f107285..edb6841 100644 --- a/django-stubs/utils/autoreload.pyi +++ b/django-stubs/utils/autoreload.pyi @@ -1,4 +1,9 @@ -from typing import Any, Callable, List, Optional +import threading +import types +from pathlib import Path +from typing import Any, Callable, List, Optional, Set, Dict, Union, Iterator, Tuple, Iterable + +from django.apps.registry import Apps USE_INOTIFY: bool fd: Any @@ -18,3 +23,45 @@ def reloader_thread() -> None: ... def restart_with_reloader() -> int: ... def python_reloader(main_func: Any, args: Any, kwargs: Any) -> None: ... def main(main_func: Any, args: Optional[Any] = ..., kwargs: Optional[Any] = ...) -> None: ... +def iter_all_python_module_files() -> Set[Path]: ... +def iter_modules_and_files( + modules: Iterable[types.ModuleType], extra_files: Iterable[Union[str, Path]] +) -> Set[Path]: ... +def common_roots(paths: Iterable[Path]) -> Iterator[Path]: ... +def sys_path_directories() -> Iterator[Path]: ... + +class BaseReloader: + extra_files: Set[Path] + directory_globs: Dict[Path, Set[str]] + def __init__(self) -> None: ... + def watch_dir(self, path: Union[str, Path], glob: str) -> None: ... + def watch_file(self, path: Union[str, Path]) -> None: ... + def watched_files(self, include_globs: bool = ...) -> Iterator[Path]: ... + def wait_for_apps_ready(self, app_reg: Apps, django_main_thread: threading.Thread) -> bool: ... + def run(self, django_main_thread: threading.Thread) -> None: ... + def run_loop(self) -> None: ... + def tick(self) -> Iterator[None]: ... + @classmethod + def check_availability(cls) -> bool: ... + def notify_file_changed(self, path: Union[str, Path]) -> None: ... + @property + def should_stop(self) -> bool: ... + def stop(self) -> None: ... + +class StatReloader(BaseReloader): + SLEEP_TIME: int = ... + def snapshot_files(self) -> Iterator[Tuple[Path, int]]: ... + +class WatchmanUnavailable(RuntimeError): ... + +class WatchmanReloader(BaseReloader): + @property + def client(self) -> Any: ... + def watched_roots(self, watched_files: Iterable[Path]) -> Set[Path]: ... + def update_watches(self) -> None: ... + def request_processed(self, **kwargs: Any) -> None: ... + def check_server_status(self, inner_ex: Optional[BaseException] = ...) -> bool: ... + +def get_reloader() -> BaseReloader: ... +def start_django(reloader: BaseReloader, main_func: Callable, *args: Any, **kwargs: Any) -> None: ... +def run_with_reloader(main_func: Callable, *args: Any, **kwargs: Any) -> None: ... diff --git a/django-stubs/utils/datastructures.pyi b/django-stubs/utils/datastructures.pyi index 11701f0..5ce243c 100644 --- a/django-stubs/utils/datastructures.pyi +++ b/django-stubs/utils/datastructures.pyi @@ -35,7 +35,7 @@ class MultiValueDict(MutableMapping[_K, _V]): def __init__(self, key_to_list_mapping: Mapping[_K, Optional[List[_V]]] = ...) -> None: ... @overload def __init__(self, key_to_list_mapping: Iterable[Tuple[_K, List[_V]]] = ...) -> None: ... - def getlist(self, key: _K, default: List[_V] = None) -> List[_V]: ... + def getlist(self, key: _K, default: Any = ...) -> List[_V]: ... def setlist(self, key: _K, list_: List[_V]) -> None: ... def setlistdefault(self, key: _K, default_list: List[_V] = None) -> List[_V]: ... def appendlist(self, key: _K, value: _V) -> None: ... @@ -51,6 +51,7 @@ class MultiValueDict(MutableMapping[_K, _V]): class ImmutableList(Tuple[_V, ...]): warning: str = ... + def __init__(self, *args: Any, warning: str = ..., **kwargs: Any) -> None: ... def complain(self, *wargs: Any, **kwargs: Any) -> None: ... class DictWrapper(Dict[str, _V]): diff --git a/django-stubs/utils/dateformat.pyi b/django-stubs/utils/dateformat.pyi index ffa1726..e9246e4 100644 --- a/django-stubs/utils/dateformat.pyi +++ b/django-stubs/utils/dateformat.pyi @@ -1,4 +1,4 @@ -from datetime import datetime +from datetime import datetime, date from typing import Any, Optional, Union from django.utils.timezone import FixedOffset @@ -59,5 +59,5 @@ class DateFormat(TimeFormat): def Y(self) -> int: ... def z(self) -> int: ... -def format(value: Union[datetime, str], format_string: str) -> str: ... +def format(value: Union[datetime, str, date], format_string: str) -> str: ... def time_format(value: Union[datetime, str], format_string: str) -> str: ... diff --git a/django-stubs/utils/decorators.pyi b/django-stubs/utils/decorators.pyi index 578049e..9fb4fcb 100644 --- a/django-stubs/utils/decorators.pyi +++ b/django-stubs/utils/decorators.pyi @@ -5,8 +5,8 @@ from django.utils.deprecation import MiddlewareMixin class classonlymethod(classmethod): ... def method_decorator(decorator: Union[Callable, Iterable[Callable]], name: str = ...) -> Callable: ... -def decorator_from_middleware_with_args(middleware_class: Type[MiddlewareMixin]) -> Callable: ... -def decorator_from_middleware(middleware_class: Type[MiddlewareMixin]) -> Callable: ... +def decorator_from_middleware_with_args(middleware_class: type) -> Callable: ... +def decorator_from_middleware(middleware_class: type) -> Callable: ... def available_attrs(fn: Any): ... def make_middleware_decorator(middleware_class: Type[MiddlewareMixin]) -> Callable: ... diff --git a/django-stubs/utils/hashable.pyi b/django-stubs/utils/hashable.pyi new file mode 100644 index 0000000..a049be9 --- /dev/null +++ b/django-stubs/utils/hashable.pyi @@ -0,0 +1,3 @@ +from typing import Any + +def make_hashable(value: Any) -> Any: ... diff --git a/django-stubs/utils/http.pyi b/django-stubs/utils/http.pyi index 3f3fd05..7a96588 100644 --- a/django-stubs/utils/http.pyi +++ b/django-stubs/utils/http.pyi @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Optional, Set, Tuple, Union +from typing import Any, Dict, List, Optional, Set, Tuple, Union, Iterable ETAG_MATCH: Any MONTHS: Any @@ -14,18 +14,20 @@ def urlquote_plus(url: str, safe: str = ...) -> str: ... def urlunquote(quoted_url: str) -> str: ... def urlunquote_plus(quoted_url: str) -> str: ... def urlencode(query: Any, doseq: bool = ...) -> str: ... -def cookie_date(epoch_seconds: float = ...) -> str: ... -def http_date(epoch_seconds: float = ...) -> str: ... +def cookie_date(epoch_seconds: Optional[float] = ...) -> str: ... +def http_date(epoch_seconds: Optional[float] = ...) -> str: ... def parse_http_date(date: str) -> int: ... def parse_http_date_safe(date: str) -> Optional[int]: ... -def base36_to_int(s: Union[Dict[int, int], Tuple[int, int, int], float, str]) -> int: ... -def int_to_base36(i: Union[Dict[int, int], Tuple[int, int, int], float, str]) -> str: ... -def urlsafe_base64_encode(s: bytes) -> bytes: ... +def base36_to_int(s: str) -> int: ... +def int_to_base36(i: int) -> str: ... +def urlsafe_base64_encode(s: bytes) -> str: ... def urlsafe_base64_decode(s: Union[bytes, str]) -> bytes: ... def parse_etags(etag_str: str) -> List[str]: ... def quote_etag(etag_str: str) -> str: ... def is_same_domain(host: str, pattern: str) -> bool: ... -def is_safe_url(url: Optional[str], allowed_hosts: Optional[Set[str]], require_https: bool = ...) -> bool: ... +def is_safe_url( + url: Optional[str], allowed_hosts: Optional[Union[str, Iterable[str]]], require_https: bool = ... +) -> bool: ... def limited_parse_qsl( qs: str, keep_blank_values: bool = ..., encoding: str = ..., errors: str = ..., fields_limit: Optional[int] = ... ) -> List[Tuple[str, str]]: ... diff --git a/django-stubs/utils/inspect.pyi b/django-stubs/utils/inspect.pyi index 44f33c4..9ae4d4f 100644 --- a/django-stubs/utils/inspect.pyi +++ b/django-stubs/utils/inspect.pyi @@ -1,8 +1,8 @@ -from typing import Any, Callable, List, Optional, Tuple +from typing import Callable, List, Tuple def get_func_args(func: Callable) -> List[str]: ... def get_func_full_args(func: Callable) -> List[Tuple[str]]: ... def func_accepts_kwargs(func: Callable) -> bool: ... def func_accepts_var_args(func: Callable) -> bool: ... -def func_has_no_args(func: Any): ... +def method_has_no_args(meth: Callable) -> bool: ... def func_supports_parameter(func: Callable, parameter: str) -> bool: ... diff --git a/django-stubs/utils/timezone.pyi b/django-stubs/utils/timezone.pyi index 36e121a..8d59235 100644 --- a/django-stubs/utils/timezone.pyi +++ b/django-stubs/utils/timezone.pyi @@ -1,6 +1,7 @@ +import types from contextlib import ContextDecorator from datetime import date, datetime as datetime, time, timedelta as timedelta, tzinfo as tzinfo -from typing import Optional, Union +from typing import Optional, Union, Type _AnyTime = Union[time, datetime] @@ -13,7 +14,7 @@ class FixedOffset(tzinfo): def __init__(self, offset: Optional[int] = ..., name: Optional[str] = ...) -> None: ... def utcoffset(self, dt: Optional[datetime]) -> Optional[timedelta]: ... def tzname(self, dt: Optional[datetime]) -> str: ... - def dst(self, dt: Optional[datetime]) -> Optional[timedelta]: ... + def dst(self, dt: Optional[Union[datetime, timedelta]]) -> Optional[timedelta]: ... class ReferenceLocalTimezone(tzinfo): STDOFFSET: timedelta = ... @@ -34,15 +35,17 @@ def get_default_timezone() -> tzinfo: ... def get_default_timezone_name() -> str: ... def get_current_timezone() -> tzinfo: ... def get_current_timezone_name() -> str: ... -def activate(timezone: tzinfo) -> None: ... +def activate(timezone: Union[tzinfo, str]) -> None: ... def deactivate() -> None: ... class override(ContextDecorator): timezone: tzinfo = ... - old_timezone: tzinfo = ... - def __init__(self, timezone: tzinfo) -> None: ... + old_timezone: Optional[tzinfo] = ... + def __init__(self, timezone: Optional[Union[str, tzinfo]]) -> None: ... def __enter__(self) -> None: ... - def __exit__(self, exc_type: object, exc_value: object, traceback: object) -> None: ... + def __exit__( + self, exc_type: Type[BaseException], exc_value: BaseException, traceback: types.TracebackType + ) -> None: ... def localtime(value: Optional[_AnyTime] = ..., timezone: Optional[tzinfo] = ...) -> datetime: ... def localdate(value: Optional[_AnyTime] = ..., timezone: Optional[tzinfo] = ...) -> date: ... diff --git a/django-stubs/utils/topological_sort.pyi b/django-stubs/utils/topological_sort.pyi new file mode 100644 index 0000000..0a537a5 --- /dev/null +++ b/django-stubs/utils/topological_sort.pyi @@ -0,0 +1,6 @@ +from typing import Any, Dict, Iterator, Set, Container, List + +class CyclicDependencyError(ValueError): ... + +def topological_sort_as_sets(dependency_graph: Dict[Any, Any]) -> Iterator[Set[Any]]: ... +def stable_topological_sort(l: Container[Any], dependency_graph: Dict[Any, Any]) -> List[Any]: ... diff --git a/django-stubs/utils/tree.pyi b/django-stubs/utils/tree.pyi index 50d8c74..74f9690 100644 --- a/django-stubs/utils/tree.pyi +++ b/django-stubs/utils/tree.pyi @@ -1,4 +1,4 @@ -from typing import Any, Dict, Iterable, Optional, Tuple, Union +from typing import Any, Dict, Iterable, Optional, Tuple, Union, Sequence from django.db.models.sql.where import NothingNode @@ -8,7 +8,7 @@ class Node: negated: bool = ... def __init__( self, - children: Optional[Iterable[Union[Node, NothingNode]]] = ..., + children: Optional[Iterable[Union[Node, NothingNode, Sequence[Any]]]] = ..., connector: Optional[str] = ..., negated: bool = ..., ) -> None: ... diff --git a/scripts/typecheck_tests.py b/scripts/typecheck_tests.py index 1a44d54..20b2ef6 100644 --- a/scripts/typecheck_tests.py +++ b/scripts/typecheck_tests.py @@ -92,7 +92,8 @@ IGNORED_ERRORS = { 'namedtuple', 'has no attribute "deconstruct"', '**Dict', - 'undefined in superclass' + 'undefined in superclass', + 'SimpleLazyObject' ], 'admin_scripts': [ 'Incompatible types in assignment (expression has type "Callable[' @@ -501,9 +502,6 @@ IGNORED_ERRORS = { + 'variable has type "SessionBase")', 'Unsupported left operand type for + ("None")', ], - 'timezones': [ - 'Too few arguments for "render" of "Template"' - ], 'test_runner': [ 'Argument "result" to "run" of "TestCase" has incompatible type "RemoteTestResult"; ' + 'expected "Optional[TestResult]"', @@ -520,6 +518,24 @@ IGNORED_ERRORS = { 'Incompatible types in assignment (expression has type "Callable[[Any, KwArg(Any)], Any]", variable has type', 'Cannot find module named \'user_commands.management.commands\'' ], + 'utils_tests': [ + 'a_package_name_that_does_not_exist', + 'Too few arguments for "__init__"', + 'Argument 1 to "activate" has incompatible type "None"; expected "Union[tzinfo, str]"', + 'Module has no attribute "content"', + 'Argument 1 to "int_to_base36" has incompatible type "object"; expected "int"', + 'Argument 1 to "base36_to_int" has incompatible type "object"; expected "str"', + 'Incompatible types in assignment (expression has type "None", base class "object" defined the type as', + 'Invalid type "Class"', + 'has no attribute "cp"', + 'Argument "name" to "cached_property" has incompatible type "int"; expected "Optional[str]"', + 'foo', + '"ImmutableList[int]" has no attribute "sort"', + 'has no attribute "cache_clear"', + 'has no attribute "cache_info"', + 'Argument 1 to "Path" has incompatible type "Optional[str]"; expected "Union[str, _PathLike[str]]"', + 'defined the type as "None"' + ], 'view_tests': [ '"EmailMessage" has no attribute "alternatives"', '"Handler" has no attribute "include_html"', @@ -729,7 +745,7 @@ TESTS_DIRS = [ # not annotatable without annotation in test # 'urlpatterns_reverse', 'user_commands', - # TODO: 'utils_tests', + 'utils_tests', 'validation', 'validators', 'version',