From fb125609818057dff8aad807637b3ca9cca5e194 Mon Sep 17 00:00:00 2001 From: Terence Honles Date: Sat, 11 Sep 2021 12:41:16 -0700 Subject: [PATCH] update all path related operations to have more accurate types (#713) --- django-stubs/contrib/staticfiles/finders.pyi | 24 ++++++++---- .../contrib/staticfiles/test_finders.yml | 39 +++++++++++++++++++ 2 files changed, 55 insertions(+), 8 deletions(-) create mode 100644 tests/typecheck/contrib/staticfiles/test_finders.yml diff --git a/django-stubs/contrib/staticfiles/finders.pyi b/django-stubs/contrib/staticfiles/finders.pyi index beabe2a..bc8b9d6 100644 --- a/django-stubs/contrib/staticfiles/finders.pyi +++ b/django-stubs/contrib/staticfiles/finders.pyi @@ -1,29 +1,34 @@ -from typing import Any, Iterable, Iterator, List, Mapping, Optional, Union, overload +import os +from typing import Any, Iterable, Iterator, List, Mapping, Optional, Tuple, Type, Union, overload from django.core.checks.messages import CheckMessage from django.core.files.storage import Storage from typing_extensions import Literal +_PathType = Union[str, bytes, os.PathLike] searched_locations: Any class BaseFinder: def check(self, **kwargs: Any) -> List[CheckMessage]: ... - def find(self, path: str, all: bool = ...) -> Optional[Any]: ... + @overload + def find(self, path: _PathType, all: Literal[True]) -> List[_PathType]: ... + @overload + def find(self, path: _PathType, all: Literal[False] = ...) -> Optional[_PathType]: ... def list(self, ignore_patterns: Any) -> Iterable[Any]: ... class FileSystemFinder(BaseFinder): - locations: List[Any] = ... + locations: List[Tuple[str, _PathType]] = ... storages: Mapping[str, Any] = ... def __init__(self, app_names: None = ..., *args: Any, **kwargs: Any) -> None: ... - def find_location(self, root: str, path: str, prefix: str = ...) -> Optional[str]: ... + def find_location(self, root: _PathType, path: _PathType, prefix: str = ...) -> Optional[_PathType]: ... class AppDirectoriesFinder(BaseFinder): - storage_class: Any = ... + storage_class: Type[Storage] = ... source_dir: str = ... apps: List[str] = ... - storages: Mapping[str, Any] = ... + storages: Mapping[str, Storage] = ... def __init__(self, app_names: None = ..., *args: Any, **kwargs: Any) -> None: ... - def find_in_app(self, app: str, path: str) -> Optional[str]: ... + def find_in_app(self, app: str, path: _PathType) -> Optional[_PathType]: ... class BaseStorageFinder(BaseFinder): storage: Storage = ... @@ -31,7 +36,10 @@ class BaseStorageFinder(BaseFinder): class DefaultStorageFinder(BaseStorageFinder): ... -def find(path: str, all: bool = ...) -> Optional[Union[List[str], str]]: ... +@overload +def find(path: str, all: Literal[True]) -> List[_PathType]: ... +@overload +def find(path: str, all: Literal[False] = ...) -> Optional[_PathType]: ... def get_finders() -> Iterator[BaseFinder]: ... @overload def get_finder(import_path: Literal["django.contrib.staticfiles.finders.FileSystemFinder"]) -> FileSystemFinder: ... diff --git a/tests/typecheck/contrib/staticfiles/test_finders.yml b/tests/typecheck/contrib/staticfiles/test_finders.yml new file mode 100644 index 0000000..da08fe7 --- /dev/null +++ b/tests/typecheck/contrib/staticfiles/test_finders.yml @@ -0,0 +1,39 @@ +- case: test_find_one + main: | + from django.contrib.staticfiles import finders + + reveal_type(finders.find("filepath")) # N: Revealed type is "Union[builtins.str, builtins.bytes, os.PathLike[Any], None]" + + for finder in finders.get_finders(): + reveal_type(finder.find("filepath")) # N: Revealed type is "Union[builtins.str, builtins.bytes, os.PathLike[Any], None]" + + reveal_type(finders.FileSystemFinder().find("filepath")) # N: Revealed type is "Union[builtins.str, builtins.bytes, os.PathLike[Any], None]" + reveal_type(finders.AppDirectoriesFinder().find("filepath")) # N: Revealed type is "Union[builtins.str, builtins.bytes, os.PathLike[Any], None]" + reveal_type(finders.DefaultStorageFinder().find("filepath")) # N: Revealed type is "Union[builtins.str, builtins.bytes, os.PathLike[Any], None]" + +- case: test_find_all + main: | + from django.contrib.staticfiles import finders + + reveal_type(finders.find("filepath", all=True)) # N: Revealed type is "builtins.list[Union[builtins.str, builtins.bytes, os.PathLike[Any]]]" + + for finder in finders.get_finders(): + reveal_type(finder.find("filepath", all=True)) # N: Revealed type is "builtins.list[Union[builtins.str, builtins.bytes, os.PathLike[Any]]]" + + reveal_type(finders.FileSystemFinder().find("filepath", all=True)) # N: Revealed type is "builtins.list[Union[builtins.str, builtins.bytes, os.PathLike[Any]]]" + reveal_type(finders.AppDirectoriesFinder().find("filepath", all=True)) # N: Revealed type is "builtins.list[Union[builtins.str, builtins.bytes, os.PathLike[Any]]]" + reveal_type(finders.DefaultStorageFinder().find("filepath", all=True)) # N: Revealed type is "builtins.list[Union[builtins.str, builtins.bytes, os.PathLike[Any]]]" + +- case: test_file_system_finder # test methods *only* on FileSystemFinder + main: | + from django.contrib.staticfiles.finders import FileSystemFinder + + finder = FileSystemFinder() + reveal_type(finder.find_location(".", "filepath")) # N: Revealed type is "Union[builtins.str, builtins.bytes, os.PathLike[Any], None]" + +- case: test_app_directories_finder # test methods *only* on AppDirectoriesFinder + main: | + from django.contrib.staticfiles.finders import AppDirectoriesFinder + + finder = AppDirectoriesFinder() + reveal_type(finder.find_in_app("app", "filepath")) # N: Revealed type is "Union[builtins.str, builtins.bytes, os.PathLike[Any], None]"