50 Commits

Author SHA1 Message Date
Maksim Kurnikov
5832605053 Remove warning about unsupported expression types (#266)
* remove warning about unsupported expression for _meta.get_field()

* lint
2019-12-12 08:20:52 +03:00
Maksim Kurnikov
31e795016f values(), values_list() with ManyToManyField (#267) 2019-12-12 08:09:47 +03:00
Maxim Kurnikov
0cba3f9fd6 bump to 1.3.1 2019-12-12 08:03:47 +03:00
Maksim Kurnikov
f02050911f various annotation improvements (#258) 2019-12-12 06:42:29 +03:00
henribru
e8e6fca78c Fix return type of refresh_from_db (#244) 2019-12-12 05:36:11 +03:00
Maksim Kurnikov
ade48b6546 Add support for BaseManager.from_queryset() (#251)
* add support for BaseManager.from_queryset()

* cleanups

* lint fixes
2019-12-12 05:35:56 +03:00
Maksim Kurnikov
b8f29027d8 Suppress IncompleteDefnException on final_iteration and continue with the loop (#260)
* suppress IncompleteDefnException on final_iteration

* lint
2019-12-12 01:04:24 +03:00
Hannes Ljungberg
eba3f6cb15 Update django.contrib.auth with Django 3.0 compatibility (#256)
* Add support for BaseBackend

* Add User.get_user_permissions

* Add support for UserManager.with_perm

* Add support for reset_url_token
2019-12-11 01:10:10 +03:00
Maksim Kurnikov
5a45544e76 Optimize tests typechecking script (#255)
* skip whole Django repo for tests typechecking

* lint
2019-12-11 00:52:08 +03:00
Maksim Kurnikov
8c2de7da56 add a number of django test directories for typecheck (#257) 2019-12-11 00:51:32 +03:00
Maksim Kurnikov
d43c6dc7e2 Merge pull request #254 from mkurnikov/better-force-text-types
Better types for django.utils.encoding
2019-12-10 23:36:38 +03:00
Maxim Kurnikov
f7e2109e06 add @overload clauses to smart_text, smart_bytes, force_bytes 2019-12-10 23:22:23 +03:00
Hannes Ljungberg
cea62abf5a Add support for database functions introduced in 3.0 (#253)
* Add support for hash database functions

* Add support for Sign
2019-12-10 22:36:11 +03:00
Hannes Ljungberg
3b69ec6a72 Add support for RangeBoundary (#252) 2019-12-10 22:09:55 +03:00
Seth Yastrov
7e794534c0 Better type for force_text using overloads/Literal
- If a str is passed in, it returns a str.
- If strings_only = True and a "protected type" is passed in, returns that type.
- Default if it doesn't match the overloads: return a str
2019-12-10 21:28:40 +03:00
Hannes Ljungberg
f5f33b061d Add support for exclusion constraints (#249) 2019-12-10 20:50:28 +03:00
Ran Benita
58b26fdbd3 Improve TestCase.assertNumQueries type (#250) 2019-12-10 19:37:13 +03:00
Youssef Moussaoui
9ca79c24a2 Move BLANK_CHOICE to django.db.models.fields (#242) 2019-12-08 08:19:24 +03:00
Maxim Kurnikov
540e28f4c6 bump version to 1.3.0 2019-12-06 23:37:19 +03:00
Maksim Kurnikov
4ac43c6ed6 Add Django 3.0 testing to CI (#246)
* add Django 3.0 testing to CI

* remove importlib_metadata usage

* conditionally load choices module for tests
2019-12-06 23:36:24 +03:00
Maksim Kurnikov
cadd6c963b fix model's on_cascade= parameter for test, update to latest gdal (#247) 2019-12-06 03:40:55 +03:00
Konstantin Alekseev
041754f817 Fix smtp backend open (#240) 2019-12-01 20:46:11 +03:00
Konstantin Alekseev
c0c5d1e588 Cleanup EmailMessage types (#208)
* Cleanup EmailMessage types

* Typecheck email module tests.
2019-12-01 17:14:16 +03:00
Maksim Kurnikov
f824003cc4 remove unused ignore pattern (#239) 2019-12-01 00:09:36 +03:00
Youssef Moussaoui
58f1833cab Declare is_relation and related_model on Field (#230) 2019-11-30 22:40:22 +03:00
Maksim Kurnikov
cbb6a7a9ac Merge pull request #238 from mkurnikov/mypy-750
Mypy 0.750 support
2019-11-30 22:27:50 +03:00
Maxim Kurnikov
2c4827bbaf properly change type of self for methods on custom manager classes 2019-11-30 22:08:16 +03:00
Maxim Kurnikov
5a151bf851 update django tests branch 2019-11-30 20:56:31 +03:00
Konstantin Alekseev
cbc7159995 Support mypy 0.750 2019-11-30 13:39:28 +03:00
Patrick Gingras
df4c17a947 added base_fields and declared_fields properties to Form (#235) 2019-11-27 22:48:05 +03:00
yaegassy
445abc046c README.md Fix: sample code for "Notes" (#234) 2019-11-26 15:40:21 +03:00
Anthony Ricaud
557b7a4fa3 Add new View.setup method introduced in Django 2.2 (#233)
https://docs.djangoproject.com/en/2.2/ref/class-based-views/base/#django.views.generic.base.View.setup
https://github.com/django/django/blob/2.2/django/views/generic/base.py#L83-L87
2019-11-20 23:49:38 +03:00
Maksim Kurnikov
8343d76895 Fix has_perm() methods for auth backend, and for contrib.auth.models (#232)
* fix has_perm() methods

* lint
2019-11-19 04:54:17 +03:00
Maksim Kurnikov
8d986a0f43 remove catch-all __getattr__ for Manager, fix some issues with manager methods (#227) 2019-11-12 20:36:07 +03:00
Pilifer
e9a90ebff0 More precise annotations of utils.timezone functions that return instances of tzinfo subclasses. (#209)
* fix annotations of utils.timezone

* use intermediary tzinfo subclass exposed in pytz typeshed

* fix annotations of get_fixed_timezone as it returns datetime.timezone in Django 2.2

* add explanatory comment to get_current_timezone annotations

* black utils.timezone.pyi
2019-11-12 18:27:54 +03:00
Christopher Sabater Cordero
7b74a6944a Add a few missing types to the stubfiles (#214)
* Add types to stub files.

* Fix black and flake8 errors.
2019-11-12 18:25:31 +03:00
Seth Yastrov
83f11a0fc6 Add Tags.translation to stub (#226) 2019-11-12 18:23:29 +03:00
Seth Yastrov
2829faf1af Both CSRF_COOKIE_SAMESITE and SESSION_COOKIE_SAMESITE should be Optional (#216) 2019-11-12 16:13:16 +03:00
src
d061e84cc7 Add HttpResponsePermanentRedirect to django.shortcuts (#211) 2019-11-12 16:12:36 +03:00
Anna Sidwell
3a9263dc62 Two small improvements (#217)
* Add return type for admin.SimpleListFilter.lookups

* force_login() can take any user, not just builtin
2019-11-12 13:31:24 +03:00
Yngve Høiseth
14aea2b4d4 Allow returning bool from test_func (#220) 2019-11-12 05:52:23 +03:00
Maksim Kurnikov
287c64d6fb Pin to 0.740 and fix CI (#225)
* update django sources

* pin mypy version, update to 0.740

* fix tests typechecking

* fix lint
2019-11-12 05:17:36 +03:00
Nikita Sobolev
6601121db2 Fixes travis url 2019-10-21 23:47:06 +03:00
Andrey
87d59c7c1a Fix django.contrib.admin.options.BaseModelAdmin.has_add_permission (#205)
* Fix `django.contrib.admin.options.BaseModelAdmin.has_add_permission` (#203).

* Add `django.db.models.base.Model.unique_error_message` (#204).
2019-10-11 12:16:21 +03:00
Maxim Kurnikov
8402e7c53e improve annotations in some places (#202)
* improve annotations in some places

* linting
2019-10-07 14:50:45 +03:00
Maxim Kurnikov
dceb075152 fix annotation for BaseCommand.handle() (#201) 2019-10-05 21:36:41 +03:00
Maxim Kurnikov
7e3f4bfa02 Fix ForeignKey type for self-reference defined in the abstract model (#200) 2019-10-05 21:36:29 +03:00
Maxim Kurnikov
db9ff6aaf6 Fix crash if model from same app referenced in RelatedField cannot be resolved (#199)
* do not crash if model from same app refd in ForeignKey cannot be resolved

* bump to 1.2.0
2019-10-05 20:00:51 +03:00
Maxim Kurnikov
717be5940f Reorganize code a bit, add current directory to sys.path (#198)
* reorganize code a bit

* add current directory to sys.path

* remove PYTHONPATH mention from the docs

* linting
2019-10-05 19:44:29 +03:00
Nikita Sobolev
b939bc96b7 Improves README with new example repo, python version matrix (#195) 2019-10-01 15:24:48 +03:00
115 changed files with 1773 additions and 835 deletions

3
.gitignore vendored
View File

@@ -9,3 +9,6 @@ build/
dist/
pip-wheel-metadata/
.pytest_cache/
/.envrc
/.direnv
django-sources/

4
.gitmodules vendored
View File

@@ -1,4 +0,0 @@
[submodule "django-sources"]
path = django-sources
url = https://github.com/django/django.git
branch = stable/2.2.x

View File

@@ -8,13 +8,20 @@ jobs:
python: 3.7
script: 'pytest'
- name: Typecheck Django test suite with python 3.7
- name: Typecheck Django 3.0 test suite with python 3.7
python: 3.7
script: 'python ./scripts/typecheck_tests.py'
script: |
python ./scripts/typecheck_tests.py --django_version=3.0
- name: Typecheck Django test suite with python 3.6
- name: Typecheck Django 3.0 test suite with python 3.6
python: 3.6
script: 'python ./scripts/typecheck_tests.py'
script: |
python ./scripts/typecheck_tests.py --django_version=3.0
- name: Typecheck Django 2.2 test suite with python 3.7
python: 3.7
script: |
python ./scripts/typecheck_tests.py --django_version=2.2
- name: Mypy for plugin code
python: 3.7
@@ -37,8 +44,9 @@ jobs:
script: 'isort --check --diff'
before_install: |
sudo apt update
sudo apt install binutils libproj-dev gdal-bin
sudo add-apt-repository ppa:ubuntugis/ubuntugis-unstable -y
sudo apt-get update
sudo apt-get install -y binutils libproj-dev gdal-bin
pip install -U pip setuptools wheel
install: |
pip install -r ./dev-requirements.txt

View File

@@ -2,7 +2,7 @@
# pep484 stubs for Django framework
[![Build Status](https://travis-ci.org/typeddjango/django-stubs.svg?branch=master)](https://travis-ci.org/typeddjango/django-stubs)
[![Build Status](https://travis-ci.com/typeddjango/django-stubs.svg?branch=master)](https://travis-ci.com/typeddjango/django-stubs)
[![Checked with mypy](http://www.mypy-lang.org/static/mypy_badge.svg)](http://mypy-lang.org/)
[![Gitter](https://badges.gitter.im/mypy-django/Lobby.svg)](https://gitter.im/mypy-django/Lobby)
@@ -22,6 +22,7 @@ pip install django-stubs
| django-stubs | mypy version | django version | python version
| ------------ | ---- | ---- | ---- |
| 1.3.0 | 0.750 | 2.2.x | ^3.6
| 1.2.0 | 0.730 | 2.2.x | ^3.6
| 1.1.0 | 0.720 | 2.2.x | ^3.6
| 0.12.x | old semantic analyzer (<0.711), dmypy support | 2.1.x | ^3.6
@@ -52,12 +53,6 @@ django_settings_module = mysettings
Where `mysettings` is a value of `DJANGO_SETTINGS_MODULE` (with or without quotes)
You might also need to explicitly tweak your `PYTHONPATH` the very same way `django` does it internally in case you have troubles with mypy / django plugin not finding your settings module. Try adding the root path of your project to your `PYTHONPATH` environment variable like so:
```bash
PYTHONPATH=${PYTHONPATH}:${PWD}
```
Current implementation uses Django runtime to extract models information, so it will crash, if your installed apps `models.py` is not correct. For this same reason, you cannot use `reveal_type` inside global scope of any Python file that will be executed for `django.setup()`.
In other words, if your `manage.py runserver` crashes, mypy will crash too.
@@ -75,7 +70,7 @@ class MyUserManager(models.Manager['MyUser']):
pass
class MyUser(models.Model):
objects = UserManager()
objects = MyUserManager()
```
work, which should make a error messages a bit better.

View File

@@ -1,7 +1,8 @@
black
pytest-mypy-plugins==1.1.0
psycopg2
flake8==3.7.8
flake8==3.7.9
flake8-pyi==19.3.0
isort==4.3.21
gitpython==3.0.5
-e .

Submodule django-sources deleted from f452d4232e

View File

@@ -1,5 +1,6 @@
from typing import Any, Iterator, Type, Optional, Dict
from django.apps.registry import Apps
from django.db.models.base import Model
MODELS_MODULE_NAME: str
@@ -7,11 +8,11 @@ MODELS_MODULE_NAME: str
class AppConfig:
name: str = ...
module: Optional[Any] = ...
apps: None = ...
apps: Optional[Apps] = ...
label: str = ...
verbose_name: str = ...
path: str = ...
models_module: None = ...
models_module: Optional[str] = ...
models: Dict[str, Type[Model]] = ...
def __init__(self, app_name: str, app_module: Optional[Any]) -> None: ...
@classmethod

View File

@@ -1,24 +1,22 @@
import threading
from collections import OrderedDict
from typing import Any, Callable, DefaultDict, Dict, Iterable, List, Optional, Tuple, Type, Union
from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple, Type, Union
from django.db.migrations.state import AppConfigStub
from django.db.models.base import Model
from .config import AppConfig
class Apps:
all_models: Dict[str, OrderedDict[str, Type[Model]]] = ...
app_configs: OrderedDict[str, AppConfig] = ...
all_models: Dict[str, Dict[str, Type[Model]]] = ...
app_configs: Dict[str, AppConfig] = ...
stored_app_configs: List[Any] = ...
apps_ready: bool = ...
ready_event: threading.Event = ...
loading: bool = ...
_pending_operations: DefaultDict[Tuple[str, str], List]
_pending_operations: Dict[Tuple[str, str], List]
models_ready: bool = ...
ready: bool = ...
def __init__(self, installed_apps: Optional[Union[List[AppConfigStub], List[str], Tuple]] = ...) -> None: ...
def populate(self, installed_apps: Union[List[AppConfigStub], List[str], Tuple] = ...) -> None: ...
def __init__(self, installed_apps: Optional[Iterable[Union[AppConfig, str]]] = ...) -> None: ...
def populate(self, installed_apps: Iterable[Union[AppConfig, str]] = ...) -> None: ...
def check_apps_ready(self) -> None: ...
def check_models_ready(self) -> None: ...
def get_app_configs(self) -> Iterable[AppConfig]: ...
@@ -31,9 +29,9 @@ class Apps:
def get_containing_app_config(self, object_name: str) -> Optional[AppConfig]: ...
def get_registered_model(self, app_label: str, model_name: str) -> Type[Model]: ...
def get_swappable_settings_name(self, to_string: str) -> Optional[str]: ...
def set_available_apps(self, available: List[str]) -> None: ...
def set_available_apps(self, available: Iterable[str]) -> None: ...
def unset_available_apps(self) -> None: ...
def set_installed_apps(self, installed: Union[List[str], Tuple[str]]) -> None: ...
def set_installed_apps(self, installed: Iterable[str]) -> None: ...
def unset_installed_apps(self) -> None: ...
def clear_cache(self) -> None: ...
def lazy_model_operation(self, function: Callable, *model_keys: Any) -> None: ...

View File

@@ -345,7 +345,7 @@ SESSION_COOKIE_PATH = "/"
SESSION_COOKIE_HTTPONLY = True
# Whether to set the flag restricting cookie leaks on cross-site requests.
# This can be 'Lax', 'Strict', or None to disable the flag.
SESSION_COOKIE_SAMESITE = "Lax"
SESSION_COOKIE_SAMESITE: Optional[str] = ...
# Whether to save the session data on every request.
SESSION_SAVE_EVERY_REQUEST = False
# Whether a user's session cookie expires when the Web browser is closed.
@@ -413,7 +413,7 @@ CSRF_COOKIE_DOMAIN = None
CSRF_COOKIE_PATH = "/"
CSRF_COOKIE_SECURE = False
CSRF_COOKIE_HTTPONLY = False
CSRF_COOKIE_SAMESITE = "Lax"
CSRF_COOKIE_SAMESITE: Optional[str] = ...
CSRF_HEADER_NAME = "HTTP_X_CSRFTOKEN"
CSRF_TRUSTED_ORIGINS: List[str] = ...
CSRF_USE_SESSIONS = False

View File

@@ -1,11 +1,13 @@
from typing import Any, List, Union
from typing import Any, List, Union, Iterable, Optional
from django.contrib.admin.options import BaseModelAdmin
from django.core.checks.messages import Error
from django.apps.config import AppConfig
_CheckError = Union[str, Error]
def check_admin_app(app_configs: None, **kwargs: Any) -> List[_CheckError]: ...
def check_admin_app(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[_CheckError]: ...
def check_dependencies(**kwargs: Any) -> List[_CheckError]: ...
class BaseModelAdminChecks:

View File

@@ -24,7 +24,7 @@ class SimpleListFilter(ListFilter):
parameter_name: Any = ...
lookup_choices: Any = ...
def value(self) -> Optional[str]: ...
def lookups(self, request: Any, model_admin: Any) -> None: ...
def lookups(self, request: Any, model_admin: Any) -> List[Tuple[Any, str]]: ...
class FieldListFilter(ListFilter):
field: Field = ...

View File

@@ -86,7 +86,7 @@ class BaseModelAdmin:
def get_sortable_by(self, request: HttpRequest) -> Union[List[Callable], List[str], Tuple]: ...
def lookup_allowed(self, lookup: str, value: str) -> bool: ...
def to_field_allowed(self, request: HttpRequest, to_field: str) -> bool: ...
def has_add_permission(self, request: HttpRequest, obj: Optional[Model] = ...) -> bool: ...
def has_add_permission(self, request: HttpRequest) -> bool: ...
def has_change_permission(self, request: HttpRequest, obj: Optional[Model] = ...) -> bool: ...
def has_delete_permission(self, request: HttpRequest, obj: Optional[Model] = ...) -> bool: ...
def has_view_permission(self, request: HttpRequest, obj: Optional[Model] = ...) -> bool: ...

View File

@@ -8,6 +8,8 @@ from django.template.response import TemplateResponse
from django.urls.resolvers import URLResolver
from django.utils.functional import LazyObject
from django.apps.config import AppConfig
all_sites: Any
class AlreadyRegistered(Exception): ...
@@ -28,7 +30,7 @@ class AdminSite:
name: str = ...
_registry: Dict[Type[Model], ModelAdmin]
def __init__(self, name: str = ...) -> None: ...
def check(self, app_configs: None) -> List[Any]: ...
def check(self, app_configs: Optional[Iterable[AppConfig]]) -> List[Any]: ...
def register(
self,
model_or_iterable: Union[Type[Model], Iterable[Type[Model]]],
@@ -37,7 +39,7 @@ class AdminSite:
) -> None: ...
def unregister(self, model_or_iterable: Union[Type[Model], Iterable[Type[Model]]]) -> None: ...
def is_registered(self, model: Type[Model]) -> bool: ...
def add_action(self, action: Callable, name: None = ...) -> None: ...
def add_action(self, action: Callable, name: Optional[str] = ...) -> None: ...
def disable_action(self, name: str) -> None: ...
def get_action(self, name: str) -> Callable: ...
@property
@@ -52,14 +54,20 @@ class AdminSite:
@property
def urls(self) -> Tuple[List[URLResolver], str, str]: ...
def each_context(self, request: Any): ...
def password_change(self, request: WSGIRequest, extra_context: Dict[str, str] = ...) -> TemplateResponse: ...
def password_change_done(self, request: WSGIRequest, extra_context: None = ...) -> TemplateResponse: ...
def password_change(
self, request: WSGIRequest, extra_context: Optional[Dict[str, Any]] = ...
) -> TemplateResponse: ...
def password_change_done(
self, request: WSGIRequest, extra_context: Optional[Dict[str, Any]] = ...
) -> TemplateResponse: ...
def i18n_javascript(self, request: WSGIRequest, extra_context: Optional[Dict[Any, Any]] = ...) -> HttpResponse: ...
def logout(self, request: WSGIRequest, extra_context: None = ...) -> TemplateResponse: ...
def login(self, request: WSGIRequest, extra_context: None = ...) -> HttpResponse: ...
def logout(self, request: WSGIRequest, extra_context: Optional[Dict[str, Any]] = ...) -> TemplateResponse: ...
def login(self, request: WSGIRequest, extra_context: Optional[Dict[str, Any]] = ...) -> HttpResponse: ...
def get_app_list(self, request: WSGIRequest) -> List[Any]: ...
def index(self, request: WSGIRequest, extra_context: Optional[Dict[str, str]] = ...) -> TemplateResponse: ...
def app_index(self, request: WSGIRequest, app_label: str, extra_context: None = ...) -> TemplateResponse: ...
def index(self, request: WSGIRequest, extra_context: Optional[Dict[str, Any]] = ...) -> TemplateResponse: ...
def app_index(
self, request: WSGIRequest, app_label: str, extra_context: Optional[Dict[str, Any]] = ...
) -> TemplateResponse: ...
class DefaultAdminSite(LazyObject): ...

View File

@@ -27,7 +27,7 @@ class ResultList(list):
def results(cl: ChangeList) -> Iterator[ResultList]: ...
def result_hidden_fields(cl: ChangeList) -> Iterator[BoundField]: ...
def result_list(
cl: ChangeList
cl: ChangeList,
) -> Dict[
str, Union[List[Dict[str, Optional[Union[int, str]]]], List[ResultList], List[BoundField], ChangeList, int]
]: ...

View File

@@ -9,12 +9,10 @@ from django.contrib.auth.forms import AdminPasswordChangeForm
from django.core.handlers.wsgi import WSGIRequest
from django.db.models.base import Model
from django.db.models.deletion import Collector
from django.db.models.fields.mixins import FieldCacheMixin
from django.db.models.fields.reverse_related import ManyToOneRel
from django.db.models.options import Options
from django.db.models.query import QuerySet
from django.forms.forms import BaseForm
from django.utils.safestring import SafeText
from django.db.models.fields import Field, reverse_related
@@ -27,7 +25,7 @@ def unquote(s: str) -> str: ...
def flatten(fields: Any) -> List[Union[Callable, str]]: ...
def flatten_fieldsets(fieldsets: Any) -> List[Union[Callable, str]]: ...
def get_deleted_objects(
objs: QuerySet, request: WSGIRequest, admin_site: AdminSite
objs: Sequence[Optional[Model]], request: WSGIRequest, admin_site: AdminSite
) -> Tuple[List[Any], Dict[Any, Any], Set[Any], List[Any]]: ...
class NestedObjects(Collector):
@@ -41,22 +39,14 @@ class NestedObjects(Collector):
model_objs: Any = ...
def __init__(self, *args: Any, **kwargs: Any) -> None: ...
def add_edge(self, source: Optional[Model], target: Model) -> None: ...
def collect(
self,
objs: Union[Sequence[Optional[Model]], QuerySet],
source: Optional[Type[Model]] = ...,
source_attr: Optional[str] = ...,
**kwargs: Any
) -> None: ...
def related_objects(self, related: ManyToOneRel, objs: List[Model]) -> QuerySet: ...
def nested(self, format_callback: Callable = ...) -> Union[List[SafeText], List[int]]: ...
def can_fast_delete(self, *args: Any, **kwargs: Any) -> bool: ...
def related_objects(self, related: ManyToOneRel, objs: Sequence[Optional[Model]]) -> QuerySet: ...
def nested(self, format_callback: Callable = ...) -> List[Any]: ...
def model_format_dict(obj: Any): ...
def model_ngettext(obj: Union[Options, QuerySet], n: Optional[int] = ...) -> str: ...
def lookup_field(
name: Union[Callable, str], obj: Model, model_admin: BaseModelAdmin = ...
) -> Tuple[Optional[Field], Callable, Callable]: ...
) -> Tuple[Optional[Field], Any, Any]: ...
def label_for_field(
name: Union[Callable, str],
model: Type[Model],
@@ -65,16 +55,14 @@ def label_for_field(
form: Optional[BaseForm] = ...,
) -> Union[Tuple[Optional[str], Union[Callable, Type[str]]], str]: ...
def help_text_for_field(name: str, model: Type[Model]) -> str: ...
def display_for_field(
value: Any, field: Union[Field, reverse_related.OneToOneRel], empty_value_display: str
) -> str: ...
def display_for_field(value: Any, field: Field, empty_value_display: str) -> str: ...
def display_for_value(value: Any, empty_value_display: str, boolean: bool = ...) -> str: ...
class NotRelationField(Exception): ...
def get_model_from_relation(field: Union[Field, reverse_related.ForeignObjectRel]) -> Type[Model]: ...
def reverse_field_path(model: Type[Model], path: str) -> Tuple[Type[Model], str]: ...
def get_fields_from_path(model: Type[Model], path: str) -> List[Union[Field, FieldCacheMixin]]: ...
def get_fields_from_path(model: Type[Model], path: str) -> List[Field]: ...
def construct_change_message(
form: AdminPasswordChangeForm, formsets: None, add: bool
) -> List[Dict[str, Dict[str, List[str]]]]: ...

View File

@@ -81,9 +81,7 @@ class ChangeList:
paginator: Any = ...
def get_results(self, request: WSGIRequest) -> None: ...
def get_ordering_field(self, field_name: Union[Callable, str]) -> Optional[Union[CombinedExpression, str]]: ...
def get_ordering(
self, request: WSGIRequest, queryset: QuerySet
) -> Union[List[Union[Combinable, str]], List[Union[OrderBy, str]]]: ...
def get_ordering(self, request: WSGIRequest, queryset: QuerySet) -> List[Union[OrderBy, Combinable, str]]: ...
def get_ordering_field_columns(self) -> OrderedDict: ...
def get_queryset(self, request: WSGIRequest) -> QuerySet: ...
def apply_select_related(self, qs: QuerySet) -> QuerySet: ...

View File

@@ -1,23 +1,34 @@
from typing import Any, Optional, Set, Union
from django.contrib.auth.base_user import AbstractBaseUser
from django.contrib.auth.models import AnonymousUser, User
from django.contrib.auth.models import AnonymousUser, User, Permission
from django.db.models.base import Model
_AnyUser = Union[Model, AnonymousUser]
UserModel: Any
class ModelBackend:
class BaseBackend:
def authenticate(
self, request: Any, username: Optional[Union[int, str]] = ..., password: Optional[str] = ..., **kwargs: Any
self, request: Any, username: Optional[str] = ..., password: Optional[str] = ..., **kwargs: Any
) -> Optional[AbstractBaseUser]: ...
def user_can_authenticate(self, user: Optional[AbstractBaseUser]) -> bool: ...
def get_user_permissions(self, user_obj: AbstractBaseUser, obj: None = ...) -> Set[str]: ...
def get_group_permissions(self, user_obj: AbstractBaseUser, obj: None = ...) -> Set[str]: ...
def get_all_permissions(self, user_obj: AbstractBaseUser, obj: Optional[str] = ...) -> Set[str]: ...
def has_perm(
self, user_obj: Union[AbstractBaseUser, AnonymousUser], perm: str, obj: Optional[str] = ...
) -> bool: ...
def has_module_perms(self, user_obj: Union[AbstractBaseUser, AnonymousUser], app_label: str) -> bool: ...
def get_user(self, user_id: int) -> AbstractBaseUser: ...
def get_user(self, user_id: int) -> Optional[AbstractBaseUser]: ...
def get_user_permissions(self, user_obj: _AnyUser, obj: Optional[Model] = ...) -> Set[str]: ...
def get_group_permissions(self, user_obj: _AnyUser, obj: Optional[Model] = ...) -> Set[str]: ...
def get_all_permissions(self, user_obj: _AnyUser, obj: Optional[Model] = ...) -> Set[str]: ...
def has_perm(self, user_obj: _AnyUser, perm: str, obj: Optional[Model] = ...) -> bool: ...
class ModelBackend(BaseBackend):
def has_module_perms(self, user_obj: _AnyUser, app_label: str) -> bool: ...
def user_can_authenticate(self, user: Optional[_AnyUser]) -> bool: ...
def with_perm(
self,
perm: Union[str, Permission],
is_active: bool = ...,
include_superusers: bool = ...,
obj: Optional[Model] = ...,
): ...
class AllowAllUsersModelBackend(ModelBackend): ...

View File

@@ -13,11 +13,10 @@ class BaseUserManager(models.Manager[_T]):
def get_by_natural_key(self, username: Optional[str]) -> _T: ...
class AbstractBaseUser(models.Model):
REQUIRED_FIELDS: List[str] = ...
password = models.CharField(max_length=128)
last_login = models.DateTimeField(blank=True, null=True)
REQUIRED_FIELDS: List[str] = ...
class Meta: ...
def get_username(self) -> str: ...
def natural_key(self) -> Tuple[str]: ...
@property

View File

@@ -1,6 +1,8 @@
from typing import Any, List
from typing import Any, List, Iterable, Optional
from django.core.checks.messages import CheckMessage
def check_user_model(app_configs: None = ..., **kwargs: Any) -> List[CheckMessage]: ...
def check_models_permissions(app_configs: None = ..., **kwargs: Any) -> List[Any]: ...
from django.apps.config import AppConfig
def check_user_model(app_configs: Optional[Iterable[AppConfig]] = ..., **kwargs: Any) -> List[CheckMessage]: ...
def check_models_permissions(app_configs: Optional[Iterable[AppConfig]] = ..., **kwargs: Any) -> List[Any]: ...

View File

@@ -1,4 +1,4 @@
from typing import Any, Callable, List
from typing import Any, Callable, List, Optional
from django import http
from django.http.response import HttpResponse, HttpResponseRedirect
@@ -23,6 +23,6 @@ class PermissionRequiredMixin(AccessMixin):
def dispatch(self, request: http.HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse: ...
class UserPassesTestMixin(AccessMixin):
def test_func(self) -> None: ...
def test_func(self) -> Optional[bool]: ...
def get_test_func(self) -> Callable: ...
def dispatch(self, request: http.HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse: ...

View File

@@ -1,5 +1,6 @@
from typing import Any, Collection, Optional, Set, Tuple, Type, TypeVar
from typing import Any, Collection, Optional, Set, Tuple, Type, TypeVar, Union
from django.contrib.auth.backends import ModelBackend
from django.contrib.auth.base_user import AbstractBaseUser as AbstractBaseUser, BaseUserManager as BaseUserManager
from django.contrib.auth.validators import UnicodeUsernameValidator
from django.contrib.contenttypes.models import ContentType
@@ -8,22 +9,28 @@ from django.db.models.manager import EmptyManager
from django.db import models
_AnyUser = Union[Model, "AnonymousUser"]
def update_last_login(sender: Type[AbstractBaseUser], user: AbstractBaseUser, **kwargs: Any) -> None: ...
class PermissionManager(models.Manager):
class PermissionManager(models.Manager["Permission"]):
def get_by_natural_key(self, codename: str, app_label: str, model: str) -> Permission: ...
class Permission(models.Model):
content_type_id: int
objects: PermissionManager
name = models.CharField(max_length=255)
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
codename = models.CharField(max_length=100)
def natural_key(self) -> Tuple[str, str, str]: ...
class GroupManager(models.Manager):
class GroupManager(models.Manager["Group"]):
def get_by_natural_key(self, name: str) -> Group: ...
class Group(models.Model):
objects: GroupManager
name = models.CharField(max_length=150)
permissions = models.ManyToManyField(Permission)
def natural_key(self): ...
@@ -37,15 +44,24 @@ class UserManager(BaseUserManager[_T]):
def create_superuser(
self, username: str, email: Optional[str], password: Optional[str], **extra_fields: Any
) -> _T: ...
def with_perm(
self,
perm: Union[str, Permission],
is_active: bool = ...,
include_superusers: bool = ...,
backend: Optional[Union[Type[ModelBackend], str]] = ...,
obj: Optional[Model] = ...,
): ...
class PermissionsMixin(models.Model):
is_superuser = models.BooleanField()
groups: models.ManyToManyField = models.ManyToManyField(Group)
user_permissions: models.ManyToManyField = models.ManyToManyField(Permission)
def get_group_permissions(self, obj: None = ...) -> Set[str]: ...
def get_all_permissions(self, obj: Optional[str] = ...) -> Set[str]: ...
def has_perm(self, perm: str, obj: Optional[str] = ...) -> bool: ...
def has_perms(self, perm_list: Collection[str], obj: None = ...) -> bool: ...
groups = models.ManyToManyField(Group)
user_permissions = models.ManyToManyField(Permission)
def get_user_permissions(self, obj: Optional[_AnyUser] = ...) -> Set[str]: ...
def get_group_permissions(self, obj: Optional[_AnyUser] = ...) -> Set[str]: ...
def get_all_permissions(self, obj: Optional[_AnyUser] = ...) -> Set[str]: ...
def has_perm(self, perm: str, obj: Optional[_AnyUser] = ...) -> bool: ...
def has_perms(self, perm_list: Collection[str], obj: Optional[_AnyUser] = ...) -> bool: ...
def has_module_perms(self, app_label: str) -> bool: ...
class AbstractUser(AbstractBaseUser, PermissionsMixin): # type: ignore
@@ -82,10 +98,11 @@ class AnonymousUser:
def groups(self) -> EmptyManager: ...
@property
def user_permissions(self) -> EmptyManager: ...
def get_group_permissions(self, obj: None = ...) -> Set[Any]: ...
def get_all_permissions(self, obj: Any = ...) -> Set[str]: ...
def has_perm(self, perm: str, obj: None = ...) -> bool: ...
def has_perms(self, perm_list: Collection[str], obj: None = ...) -> bool: ...
def get_user_permissions(self, obj: Optional[_AnyUser] = ...) -> Set[str]: ...
def get_group_permissions(self, obj: Optional[_AnyUser] = ...) -> Set[Any]: ...
def get_all_permissions(self, obj: Optional[_AnyUser] = ...) -> Set[str]: ...
def has_perm(self, perm: str, obj: Optional[_AnyUser] = ...) -> bool: ...
def has_perms(self, perm_list: Collection[str], obj: Optional[_AnyUser] = ...) -> bool: ...
def has_module_perms(self, module: str) -> bool: ...
@property
def is_anonymous(self) -> bool: ...

View File

@@ -55,6 +55,7 @@ class PasswordResetDoneView(PasswordContextMixin, TemplateView):
class PasswordResetConfirmView(PasswordContextMixin, FormView):
post_reset_login: bool = ...
post_reset_login_backend: Any = ...
reset_url_token: str = ...
title: Any = ...
token_generator: Any = ...
validlink: bool = ...

View File

@@ -1,4 +1,6 @@
from typing import Any, List
from typing import Any, List, Iterable, Optional
def check_generic_foreign_keys(app_configs: None = ..., **kwargs: Any) -> List[Any]: ...
def check_model_name_lengths(app_configs: None = ..., **kwargs: Any) -> List[Any]: ...
from django.apps.config import AppConfig
def check_generic_foreign_keys(app_configs: Optional[Iterable[AppConfig]] = ..., **kwargs: Any) -> List[Any]: ...
def check_model_name_lengths(app_configs: Optional[Iterable[AppConfig]] = ..., **kwargs: Any) -> List[Any]: ...

View File

@@ -0,0 +1,18 @@
from typing import Optional, Sequence, Tuple, Union
from django.db.models.constraints import BaseConstraint
from django.db.models.expressions import Combinable
from django.db.models.query_utils import Q
class ExclusionConstraint(BaseConstraint):
expressions: Sequence[Tuple[Union[str, Combinable], str]]
index_type: str
condition: Optional[Q]
def __init__(
self,
*,
name: str,
expressions: Sequence[Tuple[Union[str, Combinable], str]],
condition: Optional[Q] = ...,
index_type: Optional[str] = ...,
): ...

View File

@@ -8,6 +8,8 @@ from .ranges import (
FloatRangeField as FloatRangeField,
DateRangeField as DateRangeField,
DateTimeRangeField as DateTimeRangeField,
RangeOperators as RangeOperators,
RangeBoundary as RangeBoundary,
)
from .hstore import HStoreField as HStoreField
from .citext import (

View File

@@ -29,3 +29,20 @@ class DateTimeRangeField(RangeField):
class DateRangeField(RangeField):
def __get__(self, instance, owner) -> DateRange: ...
class RangeOperators:
EQUAL: str
NOT_EQUAL: str
CONTAINS: str
CONTAINED_BY: str
OVERLAPS: str
FULLY_LT: str
FULLY_GT: str
NOT_LT: str
NOT_GT: str
ADJACENT_TO: str
class RangeBoundary(models.Expression):
lower: str
upper: str
def __init__(self, inclusive_lower: bool = ..., inclusive_upper: bool = ...): ...

View File

@@ -1,10 +1,10 @@
from typing import Any
from django.apps.config import AppConfig
from django.apps.registry import Apps
from django.contrib.sites.apps import SitesConfig
def create_default_site(
app_config: SitesConfig,
app_config: AppConfig,
verbosity: int = ...,
interactive: bool = ...,
using: str = ...,

View File

@@ -6,15 +6,16 @@ from django.db import models
SITE_CACHE: Any
class SiteManager(models.Manager):
class SiteManager(models.Manager["Site"]):
def get_current(self, request: Optional[HttpRequest] = ...) -> Site: ...
def clear_cache(self) -> None: ...
def get_by_natural_key(self, domain: str) -> Site: ...
class Site(models.Model):
domain: models.CharField = ...
name: models.CharField = ...
objects: SiteManager = ...
objects: SiteManager
domain = models.CharField(max_length=100)
name = models.CharField(max_length=50)
def natural_key(self) -> Tuple[str]: ...
def clear_site_cache(sender: Type[Site], **kwargs: Any) -> None: ...

View File

@@ -1,5 +1,7 @@
from typing import Any, List
from typing import Any, List, Iterable, Optional
from django.core.checks.messages import Error
def check_finders(app_configs: None = ..., **kwargs: Any) -> List[Error]: ...
from django.apps.config import AppConfig
def check_finders(app_configs: Optional[Iterable[AppConfig]] = ..., **kwargs: Any) -> List[Error]: ...

View File

@@ -37,7 +37,7 @@ def get_finders() -> Iterator[BaseFinder]: ...
def get_finder(import_path: Literal["django.contrib.staticfiles.finders.FileSystemFinder"]) -> FileSystemFinder: ...
@overload
def get_finder(
import_path: Literal["django.contrib.staticfiles.finders.AppDirectoriesFinder"]
import_path: Literal["django.contrib.staticfiles.finders.AppDirectoriesFinder"],
) -> AppDirectoriesFinder: ...
@overload
def get_finder(import_path: str) -> BaseFinder: ...

View File

@@ -1,7 +1,9 @@
from typing import Any, List
from typing import Any, List, Iterable, Optional
from django.core.checks.messages import Error
from django.apps.config import AppConfig
E001: Any
def check_default_cache_is_configured(app_configs: None, **kwargs: Any) -> List[Error]: ...
def check_default_cache_is_configured(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Error]: ...

View File

@@ -1,6 +1,8 @@
from typing import Any, List
from typing import Any, List, Iterable, Optional
from django.core.checks.messages import Warning
def check_all_models(app_configs: None = ..., **kwargs: Any) -> List[Warning]: ...
def check_lazy_references(app_configs: None = ..., **kwargs: Any) -> List[Any]: ...
from django.apps.config import AppConfig
def check_all_models(app_configs: Optional[Iterable[AppConfig]] = ..., **kwargs: Any) -> List[Warning]: ...
def check_lazy_references(app_configs: Optional[Iterable[AppConfig]] = ..., **kwargs: Any) -> List[Any]: ...

View File

@@ -12,6 +12,7 @@ class Tags:
security: str = ...
signals: str = ...
templates: str = ...
translation: str = ...
urls: str = ...
class CheckRegistry:

View File

@@ -1,7 +1,9 @@
from typing import Any, List
from typing import Any, List, Iterable, Optional
from django.core.checks.messages import Warning
from django.apps.config import AppConfig
SECRET_KEY_MIN_LENGTH: int
SECRET_KEY_MIN_UNIQUE_CHARACTERS: int
W001: Any
@@ -17,15 +19,15 @@ W019: Any
W020: Any
W021: Any
def check_security_middleware(app_configs: None, **kwargs: Any) -> List[Warning]: ...
def check_xframe_options_middleware(app_configs: None, **kwargs: Any) -> List[Warning]: ...
def check_sts(app_configs: None, **kwargs: Any) -> List[Warning]: ...
def check_sts_include_subdomains(app_configs: None, **kwargs: Any) -> List[Warning]: ...
def check_sts_preload(app_configs: None, **kwargs: Any) -> List[Warning]: ...
def check_content_type_nosniff(app_configs: None, **kwargs: Any) -> List[Warning]: ...
def check_xss_filter(app_configs: None, **kwargs: Any) -> List[Warning]: ...
def check_ssl_redirect(app_configs: None, **kwargs: Any) -> List[Warning]: ...
def check_secret_key(app_configs: None, **kwargs: Any) -> List[Warning]: ...
def check_debug(app_configs: None, **kwargs: Any) -> List[Warning]: ...
def check_xframe_deny(app_configs: None, **kwargs: Any) -> List[Warning]: ...
def check_allowed_hosts(app_configs: None, **kwargs: Any) -> List[Warning]: ...
def check_security_middleware(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Warning]: ...
def check_xframe_options_middleware(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Warning]: ...
def check_sts(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Warning]: ...
def check_sts_include_subdomains(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Warning]: ...
def check_sts_preload(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Warning]: ...
def check_content_type_nosniff(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Warning]: ...
def check_xss_filter(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Warning]: ...
def check_ssl_redirect(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Warning]: ...
def check_secret_key(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Warning]: ...
def check_debug(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Warning]: ...
def check_xframe_deny(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Warning]: ...
def check_allowed_hosts(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Warning]: ...

View File

@@ -1,9 +1,11 @@
from typing import Any, List
from typing import Any, List, Iterable, Optional
from django.core.checks.messages import Warning
from django.apps.config import AppConfig
W003: Any
W016: Any
def check_csrf_middleware(app_configs: None, **kwargs: Any) -> List[Warning]: ...
def check_csrf_cookie_secure(app_configs: None, **kwargs: Any) -> List[Warning]: ...
def check_csrf_middleware(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Warning]: ...
def check_csrf_cookie_secure(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Warning]: ...

View File

@@ -1,7 +1,9 @@
from typing import Any, List
from typing import Any, List, Iterable, Optional
from django.core.checks.messages import Warning
from django.apps.config import AppConfig
def add_session_cookie_message(message: Any): ...
W010: Any
@@ -14,5 +16,5 @@ W013: Any
W014: Any
W015: Any
def check_session_cookie_secure(app_configs: None, **kwargs: Any) -> List[Warning]: ...
def check_session_cookie_httponly(app_configs: None, **kwargs: Any) -> List[Warning]: ...
def check_session_cookie_secure(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Warning]: ...
def check_session_cookie_httponly(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Warning]: ...

View File

@@ -1,9 +1,11 @@
from typing import Any, List
from typing import Any, List, Iterable, Optional
from django.core.checks.messages import Error
from django.apps.config import AppConfig
E001: Any
E002: Any
def check_setting_app_dirs_loaders(app_configs: None, **kwargs: Any) -> List[Error]: ...
def check_string_if_invalid_is_string(app_configs: None, **kwargs: Any) -> List[Error]: ...
def check_setting_app_dirs_loaders(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Error]: ...
def check_string_if_invalid_is_string(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Error]: ...

View File

@@ -1,11 +1,13 @@
from typing import Any, Callable, List, Tuple, Union
from typing import Any, Callable, List, Tuple, Union, Iterable, Optional
from django.core.checks.messages import CheckMessage, Error, Warning
from django.urls.resolvers import URLPattern, URLResolver
def check_url_config(app_configs: None, **kwargs: Any) -> List[CheckMessage]: ...
from django.apps.config import AppConfig
def check_url_config(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[CheckMessage]: ...
def check_resolver(resolver: Union[Tuple[str, Callable], URLPattern, URLResolver]) -> List[CheckMessage]: ...
def check_url_namespaces_unique(app_configs: None, **kwargs: Any) -> List[Warning]: ...
def check_url_namespaces_unique(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Warning]: ...
def get_warning_for_invalid_pattern(pattern: Any) -> List[Error]: ...
def check_url_settings(app_configs: None, **kwargs: Any) -> List[Error]: ...
def check_url_settings(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Error]: ...
def E006(name: str) -> Error: ...

View File

@@ -1,5 +1,5 @@
import types
from typing import Any, TypeVar, Type, Iterable
from typing import Any, TypeVar, Type, Iterable, Optional
from django.core.mail.message import EmailMessage
@@ -7,7 +7,7 @@ _T = TypeVar("_T", bound="BaseEmailBackend")
class BaseEmailBackend:
def __init__(self, fail_silently: bool = ..., **kwargs: Any) -> None: ...
def open(self) -> bool: ...
def open(self) -> Optional[bool]: ...
def close(self) -> None: ...
def __enter__(self: _T) -> _T: ...
def __exit__(

View File

@@ -0,0 +1,3 @@
from django.core.mail.backends.base import BaseEmailBackend
class EmailBackend(BaseEmailBackend): ...

View File

@@ -0,0 +1,3 @@
from django.core.mail.backends.base import BaseEmailBackend
class EmailBackend(BaseEmailBackend): ...

View File

@@ -0,0 +1,3 @@
from django.core.mail.backends.base import BaseEmailBackend
class EmailBackend(BaseEmailBackend): ...

View File

@@ -0,0 +1,3 @@
from django.core.mail.backends.base import BaseEmailBackend
class EmailBackend(BaseEmailBackend): ...

View File

@@ -0,0 +1,18 @@
import smtplib
import threading
from typing import Optional, Union
from django.core.mail.backends.base import BaseEmailBackend
class EmailBackend(BaseEmailBackend):
host: str = ...
port: int = ...
username: str = ...
password: str = ...
use_tls: bool = ...
use_ssl: bool = ...
timeout: Optional[int] = ...
ssl_keyfile: Optional[str] = ...
ssl_certfile: Optional[str] = ...
connection: Union[smtplib.SMTP_SSL, smtplib.SMTP, None] = ...
_lock: threading.RLock = ...

View File

@@ -1,8 +1,10 @@
from email._policybase import Policy # type: ignore
from email.message import Message
from email.mime.base import MIMEBase
from email.mime.message import MIMEMessage
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from typing import Any, Dict, List, Optional, Sequence, Tuple, Union
from typing import Any, Dict, List, Optional, Sequence, Tuple, Union, overload
utf8_charset: Any
utf8_charset_qp: Any
@@ -43,6 +45,11 @@ class SafeMIMEMultipart(MIMEMixin, MIMEMultipart):
self, _subtype: str = ..., boundary: None = ..., _subparts: None = ..., encoding: str = ..., **_params: Any
) -> None: ...
_AttachmentContent = Union[bytes, EmailMessage, Message, SafeMIMEText, str]
_AttachmentTuple = Union[
Tuple[str, _AttachmentContent], Tuple[Optional[str], _AttachmentContent, str], Tuple[str, _AttachmentContent, None]
]
class EmailMessage:
content_subtype: str = ...
mixed_subtype: str = ...
@@ -62,42 +69,42 @@ class EmailMessage:
subject: str = ...,
body: Optional[str] = ...,
from_email: Optional[str] = ...,
to: Optional[Union[Sequence[str], str]] = ...,
bcc: Optional[Union[Sequence[str], str]] = ...,
to: Optional[Sequence[str]] = ...,
bcc: Optional[Sequence[str]] = ...,
connection: Optional[Any] = ...,
attachments: Optional[Union[List[Tuple[str, Union[str, bytes], str]], List[MIMEText]]] = ...,
attachments: Optional[Sequence[Union[MIMEBase, _AttachmentTuple]]] = ...,
headers: Optional[Dict[str, str]] = ...,
cc: Optional[Union[Sequence[str], str]] = ...,
reply_to: Optional[Union[List[Optional[str]], str]] = ...,
cc: Optional[Sequence[str]] = ...,
reply_to: Optional[Sequence[str]] = ...,
) -> None: ...
def get_connection(self, fail_silently: bool = ...) -> Any: ...
# TODO: when typeshed gets more types for email.Message, move it to MIMEMessage, now it has too many false-positives
def message(self) -> Any: ...
def recipients(self) -> List[str]: ...
def send(self, fail_silently: bool = ...) -> int: ...
def attach(
self,
filename: Optional[Union[MIMEText, str]] = ...,
content: Optional[Union[bytes, EmailMessage, SafeMIMEText, str]] = ...,
mimetype: Optional[str] = ...,
) -> None: ...
@overload
def attach(self, filename: MIMEText = ...) -> None: ...
@overload
def attach(self, filename: None = ..., content: _AttachmentContent = ..., mimetype: str = ...) -> None: ...
@overload
def attach(self, filename: str = ..., content: _AttachmentContent = ..., mimetype: Optional[str] = ...) -> None: ...
def attach_file(self, path: str, mimetype: Optional[str] = ...) -> None: ...
class EmailMultiAlternatives(EmailMessage):
alternative_subtype: str = ...
alternatives: Any = ...
alternatives: Sequence[Tuple[_AttachmentContent, str]] = ...
def __init__(
self,
subject: str = ...,
body: str = ...,
from_email: Optional[str] = ...,
to: Optional[List[str]] = ...,
bcc: Optional[List[str]] = ...,
to: Optional[Sequence[str]] = ...,
bcc: Optional[Sequence[str]] = ...,
connection: Optional[Any] = ...,
attachments: None = ...,
attachments: Optional[Sequence[Union[MIMEBase, _AttachmentTuple]]] = ...,
headers: Optional[Dict[str, str]] = ...,
alternatives: Optional[List[Tuple[str, str]]] = ...,
cc: None = ...,
reply_to: None = ...,
alternatives: Optional[Sequence[Tuple[_AttachmentContent, str]]] = ...,
cc: Optional[Sequence[str]] = ...,
reply_to: Optional[Sequence[str]] = ...,
) -> None: ...
def attach_alternative(self, content: str, mimetype: str) -> None: ...
def attach_alternative(self, content: _AttachmentContent, mimetype: str) -> None: ...

View File

@@ -63,7 +63,7 @@ class BaseCommand:
fail_level: int = ...,
) -> None: ...
def check_migrations(self) -> None: ...
def handle(self, *args: Any, **options: Any) -> None: ...
def handle(self, *args: Any, **options: Any) -> Optional[str]: ...
class AppCommand(BaseCommand):
missing_args_message: str = ...

View File

@@ -1,6 +1,6 @@
from datetime import date
from io import BufferedReader, StringIO, TextIOWrapper
from typing import Any, Dict, Iterable, List, Mapping, Optional, Type, Union
from typing import Any, Dict, Iterable, List, Mapping, Optional, Type, Union, Collection
from uuid import UUID
from django.core.management.base import OutputWrapper
@@ -35,18 +35,18 @@ class Serializer:
internal_use_only: bool = ...
progress_class: Any = ...
stream_class: Any = ...
options: Any = ...
options: Dict[str, Any] = ...
stream: Any = ...
selected_fields: Any = ...
use_natural_foreign_keys: Any = ...
use_natural_primary_keys: Any = ...
selected_fields: Optional[Collection[str]] = ...
use_natural_foreign_keys: bool = ...
use_natural_primary_keys: bool = ...
first: bool = ...
def serialize(
self,
queryset: Iterable[Model],
*,
stream: Optional[Any] = ...,
fields: Optional[Any] = ...,
fields: Optional[Collection[str]] = ...,
use_natural_foreign_keys: bool = ...,
use_natural_primary_keys: bool = ...,
progress_output: Optional[Any] = ...,
@@ -63,7 +63,7 @@ class Serializer:
def getvalue(self) -> Optional[Union[bytes, str]]: ...
class Deserializer:
options: Any = ...
options: Dict[str, Any] = ...
stream: Any = ...
def __init__(self, stream_or_string: Union[BufferedReader, TextIOWrapper, str], **options: Any) -> None: ...
def __iter__(self) -> Deserializer: ...

View File

@@ -1,24 +1,10 @@
import json
from datetime import datetime
from decimal import Decimal
from io import TextIOWrapper
from typing import Any, Union, Dict
from uuid import UUID
from typing import Any, Dict
from django.core.serializers.python import Serializer as PythonSerializer
from django.db.models.base import Model
class Serializer(PythonSerializer):
json_kwargs: Dict[str, Any]
options: Dict[str, None]
selected_fields: None
stream: TextIOWrapper
use_natural_foreign_keys: bool
use_natural_primary_keys: bool
internal_use_only: bool = ...
def start_serialization(self) -> None: ...
def end_serialization(self) -> None: ...
def end_object(self, obj: Model) -> None: ...
def Deserializer(stream_or_string: Any, **options: Any) -> None: ...
@@ -29,4 +15,3 @@ class DjangoJSONEncoder(json.JSONEncoder):
indent: int
skipkeys: bool
sort_keys: bool
def default(self, o: Union[datetime, Decimal, UUID]) -> str: ...

View File

@@ -1,31 +1,15 @@
from collections import OrderedDict
from io import TextIOWrapper
from typing import Any, Dict, Iterator, List
from typing import Any, Dict, Iterator, List, Optional
from django.core.serializers.base import DeserializedObject
from django.db.models.base import Model
from django.db.models.fields.related import ForeignKey, ManyToManyField
from django.core.serializers import base
from django.db.models.fields import Field
class Serializer(base.Serializer):
options: Dict[Any, Any]
selected_fields: None
stream: TextIOWrapper
use_natural_foreign_keys: bool
use_natural_primary_keys: bool
internal_use_only: bool = ...
objects: List[Any] = ...
def start_serialization(self) -> None: ...
def end_serialization(self) -> None: ...
def start_object(self, obj: Model) -> None: ...
def end_object(self, obj: Model) -> None: ...
def get_dump_object(self, obj: Model) -> OrderedDict: ...
def handle_field(self, obj: Model, field: Field) -> None: ...
def handle_fk_field(self, obj: Model, field: ForeignKey) -> None: ...
def handle_m2m_field(self, obj: Model, field: ManyToManyField) -> None: ...
def Deserializer(
object_list: List[Dict[str, Any]], *, using: Any = ..., ignorenonexistent: bool = ..., **options: Any
object_list: List[Dict[str, Any]], *, using: Optional[str] = ..., ignorenonexistent: bool = ..., **options: Any
) -> Iterator[DeserializedObject]: ...

View File

@@ -1,15 +1,13 @@
from datetime import datetime
from decimal import Decimal
from re import RegexFlag
from typing import Any, Dict, List, Optional, Union, Pattern, Collection
from uuid import UUID
from typing import Any, Dict, List, Optional, Union, Pattern, Collection, Callable, Tuple
from django.core.files.base import File
from django.core.exceptions import ValidationError as ValidationError # noqa: F401
EMPTY_VALUES: Any
_Regex = Union[str, Pattern[str]]
_ErrorMessage = Union[str, Any]
def _lazy_re_compile(regex: _Regex, flags: int = ...): ...
@@ -22,7 +20,7 @@ class RegexValidator:
def __init__(
self,
regex: Optional[_Regex] = ...,
message: Optional[str] = ...,
message: Optional[_ErrorMessage] = ...,
code: Optional[str] = ...,
inverse_match: Optional[bool] = ...,
flags: Optional[RegexFlag] = ...,
@@ -45,67 +43,53 @@ integer_validator: Any
def validate_integer(value: Optional[Union[float, str]]) -> None: ...
class EmailValidator:
message: Any = ...
message: str = ...
code: str = ...
user_regex: Any = ...
domain_regex: Any = ...
literal_regex: Any = ...
domain_whitelist: Any = ...
def __init__(
self, message: Optional[str] = ..., code: Optional[str] = ..., whitelist: Optional[Collection[str]] = ...
self,
message: Optional[_ErrorMessage] = ...,
code: Optional[str] = ...,
whitelist: Optional[Collection[str]] = ...,
) -> None: ...
def __call__(self, value: Optional[str]) -> None: ...
def validate_domain_part(self, domain_part: str) -> bool: ...
validate_email: Any
slug_re: Any
validate_slug: Any
slug_unicode_re: Any
validate_unicode_slug: Any
validate_email: EmailValidator = ...
slug_re: Pattern = ...
validate_slug: RegexValidator = ...
slug_unicode_re: Pattern = ...
validate_unicode_slug: RegexValidator = ...
def validate_ipv4_address(value: str) -> None: ...
def validate_ipv6_address(value: str) -> None: ...
def validate_ipv46_address(value: str) -> None: ...
ip_address_validator_map: Any
ip_address_validator_map: Dict[str, Tuple[Callable[[Any], None], str]]
def ip_address_validators(protocol: str, unpack_ipv4: bool) -> Any: ...
def int_list_validator(
sep: str = ..., message: None = ..., code: str = ..., allow_negative: bool = ...
sep: str = ..., message: Optional[_ErrorMessage] = ..., code: str = ..., allow_negative: bool = ...
) -> RegexValidator: ...
validate_comma_separated_integer_list: Any
class BaseValidator:
message: Any = ...
message: str = ...
code: str = ...
limit_value: Any = ...
def __init__(self, limit_value: Any, message: Optional[str] = ...) -> None: ...
def __init__(self, limit_value: Any, message: Optional[_ErrorMessage] = ...) -> None: ...
def __call__(self, value: Any) -> None: ...
def compare(self, a: bool, b: bool) -> bool: ...
def compare(self, a: Any, b: Any) -> bool: ...
def clean(self, x: Any) -> Any: ...
class MaxValueValidator(BaseValidator):
message: Any = ...
code: str = ...
def compare(self, a: Union[datetime, Decimal, float], b: Union[datetime, Decimal, float]) -> bool: ...
class MinValueValidator(BaseValidator):
message: Any = ...
code: str = ...
def compare(self, a: Union[datetime, Decimal, float], b: Union[datetime, Decimal, float]) -> bool: ...
class MinLengthValidator(BaseValidator):
message: Any = ...
code: str = ...
def compare(self, a: int, b: int) -> bool: ...
def clean(self, x: str) -> int: ...
class MaxLengthValidator(BaseValidator):
message: Any = ...
code: str = ...
def compare(self, a: int, b: int) -> bool: ...
def clean(self, x: Union[bytes, str]) -> int: ...
class MaxValueValidator(BaseValidator): ...
class MinValueValidator(BaseValidator): ...
class MinLengthValidator(BaseValidator): ...
class MaxLengthValidator(BaseValidator): ...
class DecimalValidator:
messages: Any = ...
@@ -115,13 +99,13 @@ class DecimalValidator:
def __call__(self, value: Decimal) -> None: ...
class FileExtensionValidator:
message: Any = ...
message: str = ...
code: str = ...
allowed_extensions: List[str] = ...
def __init__(
self,
allowed_extensions: Optional[Collection[str]] = ...,
message: Optional[str] = ...,
message: Optional[_ErrorMessage] = ...,
code: Optional[str] = ...,
) -> None: ...
def __call__(self, value: File) -> None: ...
@@ -130,7 +114,7 @@ def get_available_image_extensions() -> List[str]: ...
def validate_image_file_extension(value: File) -> None: ...
class ProhibitNullCharactersValidator:
message: Any = ...
message: str = ...
code: str = ...
def __init__(self, message: Optional[str] = ..., code: Optional[str] = ...) -> None: ...
def __call__(self, value: Optional[Union[Dict[Any, Any], str, UUID]]) -> None: ...
def __init__(self, message: Optional[_ErrorMessage] = ..., code: Optional[str] = ...) -> None: ...
def __call__(self, value: Any) -> None: ...

View File

@@ -20,7 +20,7 @@ class BaseDatabaseWrapper:
ops: Any = ...
vendor: str = ...
display_name: str = ...
SchemaEditorClass: Any = ...
SchemaEditorClass: Optional[BaseDatabaseSchemaEditor] = ...
client_class: Any = ...
creation_class: Any = ...
features_class: Any = ...

View File

@@ -1,5 +1,5 @@
from collections import namedtuple
from typing import Any, Dict, List, Optional, Set, Tuple, Type, Union
from typing import Any, Dict, List, Optional, Set, Type
from django.db.backends.base.base import BaseDatabaseWrapper
from django.db.backends.utils import CursorWrapper
@@ -13,7 +13,7 @@ class BaseDatabaseIntrospection:
data_types_reverse: Any = ...
connection: Any = ...
def __init__(self, connection: BaseDatabaseWrapper) -> None: ...
def get_field_type(self, data_type: str, description: FieldInfo) -> Union[Tuple[str, Dict[str, int]], str]: ...
def get_field_type(self, data_type: str, description: FieldInfo) -> str: ...
def table_name_converter(self, name: str) -> str: ...
def column_name_converter(self, name: str) -> str: ...
def table_names(self, cursor: Optional[CursorWrapper] = ..., include_views: bool = ...) -> List[str]: ...

View File

@@ -1,10 +1,9 @@
from datetime import date, datetime, timedelta
from datetime import date, datetime, timedelta, time
from decimal import Decimal
from typing import Any, List, Optional, Sequence, Tuple, Type, Union
from django.core.management.color import Style
from django.db.backends.base.base import BaseDatabaseWrapper
from django.db.backends.sqlite3.base import DatabaseWrapper
from django.db.backends.utils import CursorWrapper
from django.db.models.base import Model
from django.db.models.expressions import Case, Expression
@@ -13,6 +12,8 @@ from django.db.models.sql.compiler import SQLCompiler
from django.db import DefaultConnectionProxy
from django.db.models.fields import Field
_Connection = Union[DefaultConnectionProxy, BaseDatabaseWrapper]
class BaseDatabaseOperations:
compiler_module: str = ...
integer_field_ranges: Any = ...
@@ -25,8 +26,8 @@ class BaseDatabaseOperations:
UNBOUNDED_FOLLOWING: Any = ...
CURRENT_ROW: str = ...
explain_prefix: Any = ...
connection: Any = ...
def __init__(self, connection: Optional[Union[DefaultConnectionProxy, BaseDatabaseWrapper]]) -> None: ...
connection: _Connection = ...
def __init__(self, connection: Optional[_Connection]) -> None: ...
def autoinc_sql(self, table: str, column: str) -> None: ...
def bulk_batch_size(self, fields: Any, objs: Any): ...
def cache_key_culling_sql(self) -> str: ...
@@ -75,10 +76,10 @@ class BaseDatabaseOperations:
def prep_for_like_query(self, x: str) -> str: ...
prep_for_iexact_query: Any = ...
def validate_autopk_value(self, value: int) -> int: ...
def adapt_unknown_value(self, value: Union[datetime, Decimal, int, str]) -> Union[int, str]: ...
def adapt_unknown_value(self, value: Any) -> Any: ...
def adapt_datefield_value(self, value: Optional[date]) -> Optional[str]: ...
def adapt_datetimefield_value(self, value: None) -> None: ...
def adapt_timefield_value(self, value: Optional[datetime]) -> Optional[str]: ...
def adapt_datetimefield_value(self, value: Optional[datetime]) -> Optional[str]: ...
def adapt_timefield_value(self, value: Optional[Union[datetime, time]]) -> Optional[str]: ...
def adapt_decimalfield_value(
self, value: Optional[Decimal], max_digits: Optional[int] = ..., decimal_places: Optional[int] = ...
) -> Optional[str]: ...
@@ -87,19 +88,17 @@ class BaseDatabaseOperations:
def year_lookup_bounds_for_datetime_field(self, value: int) -> List[str]: ...
def get_db_converters(self, expression: Expression) -> List[Any]: ...
def convert_durationfield_value(
self, value: Optional[float], expression: Expression, connection: DatabaseWrapper
self, value: Optional[float], expression: Expression, connection: _Connection
) -> Optional[timedelta]: ...
def check_expression_support(self, expression: Any) -> None: ...
def combine_expression(self, connector: str, sub_expressions: List[str]) -> str: ...
def combine_duration_expression(self, connector: Any, sub_expressions: Any): ...
def binary_placeholder_sql(self, value: Optional[Case]) -> str: ...
def modify_insert_params(
self, placeholder: str, params: Union[List[None], List[bool], List[float], List[str]]
) -> Union[List[None], List[bool], List[float], List[str]]: ...
def modify_insert_params(self, placeholder: str, params: Any) -> Any: ...
def integer_field_range(self, internal_type: Any): ...
def subtract_temporals(self, internal_type: Any, lhs: Any, rhs: Any): ...
def window_frame_start(self, start: Any): ...
def window_frame_end(self, end: Any): ...
def window_frame_rows_start_end(self, start: None = ..., end: None = ...) -> Any: ...
def window_frame_range_start_end(self, start: Optional[Any] = ..., end: Optional[Any] = ...): ...
def window_frame_rows_start_end(self, start: Optional[int] = ..., end: Optional[int] = ...) -> Any: ...
def window_frame_range_start_end(self, start: Optional[int] = ..., end: Optional[int] = ...) -> Any: ...
def explain_query_prefix(self, format: Optional[str] = ..., **options: Any) -> str: ...

View File

@@ -48,7 +48,7 @@ class BaseDatabaseSchemaEditor(ContextManager[Any]):
def quote_name(self, name: str) -> str: ...
def column_sql(
self, model: Type[Model], field: Field, include_default: bool = ...
) -> Union[Tuple[None, None], Tuple[str, List[Any]]]: ...
) -> Tuple[Optional[str], Optional[List[Any]]]: ...
def skip_default(self, field: Any): ...
def prepare_default(self, value: Any) -> None: ...
def effective_default(self, field: Field) -> Optional[Union[int, str]]: ...

View File

@@ -10,3 +10,5 @@ class DatabaseWrapper(BaseDatabaseWrapper): ...
FORMAT_QMARK_REGEX: Any
class SQLiteCursorWrapper(Database.Cursor): ...
def check_sqlite_version() -> None: ...

View File

@@ -1,4 +1,4 @@
from typing import Any, Dict, Optional, Tuple, Union
from typing import Any, Optional
from django.db.backends.base.introspection import BaseDatabaseIntrospection
@@ -8,6 +8,6 @@ def get_field_size(name: str) -> Optional[int]: ...
class FlexibleFieldLookupDict:
base_data_types_reverse: Any = ...
def __getitem__(self, key: str) -> Union[Tuple[str, Dict[str, int]], str]: ...
def __getitem__(self, key: str) -> Any: ...
class DatabaseIntrospection(BaseDatabaseIntrospection): ...

View File

@@ -1,21 +1,13 @@
from typing import Any, DefaultDict, Dict, Iterator, List, Optional, Sequence, Tuple, Type, Union, Set
from typing import Any, Dict, Iterator, List, Optional, Sequence, Tuple, Type, Union, Set
from django.apps import AppConfig
from django.apps.registry import Apps
from django.db.models.base import Model
from django.db.models.manager import Manager
from django.db.models.fields import Field
class AppConfigStub:
apps: None
label: str
models: None
models_module: None
module: None
name: str
verbose_name: str
def __init__(self, label: str) -> None: ...
def import_models(self) -> None: ...
class AppConfigStub(AppConfig): ...
class ModelState:
name: str
@@ -66,13 +58,7 @@ class ProjectState:
def remove_model(self, app_label: str, model_name: str) -> None: ...
class StateApps(Apps):
all_models: DefaultDict
apps_ready: bool
loading: bool
models_ready: bool
ready: bool
real_models: List[ModelState]
stored_app_configs: List[Any]
def __init__(
self, real_apps: List[str], models: Dict[Tuple[str, str], ModelState], ignore_swappable: bool = ...
) -> None: ...

View File

@@ -40,6 +40,7 @@ from .fields import (
DurationField as DurationField,
BigAutoField as BigAutoField,
CommaSeparatedIntegerField as CommaSeparatedIntegerField,
NOT_PROVIDED as NOT_PROVIDED,
)
from .fields.related import (
@@ -128,3 +129,5 @@ from .constraints import (
CheckConstraint as CheckConstraint,
UniqueConstraint as UniqueConstraint,
)
from .enums import Choices as Choices, IntegerChoices as IntegerChoices, TextChoices as TextChoices

View File

@@ -1,6 +1,7 @@
from typing import Any, Dict, List, Optional, Sequence, Set, Tuple, TypeVar, Union
from typing import Any, Callable, Dict, List, Optional, Sequence, Set, Tuple, Type, TypeVar, Union, Collection
from django.core.checks.messages import CheckMessage
from django.core.exceptions import ValidationError
from django.db.models.manager import Manager
from django.db.models.options import Options
@@ -18,10 +19,13 @@ class Model(metaclass=ModelBase):
pk: Any = ...
def __init__(self: _Self, *args, **kwargs) -> None: ...
def delete(self, using: Any = ..., keep_parents: bool = ...) -> Tuple[int, Dict[str, int]]: ...
def full_clean(self, exclude: Optional[List[str]] = ..., validate_unique: bool = ...) -> None: ...
def full_clean(self, exclude: Optional[Collection[str]] = ..., validate_unique: bool = ...) -> None: ...
def clean(self) -> None: ...
def clean_fields(self, exclude: List[str] = ...) -> None: ...
def validate_unique(self, exclude: List[str] = ...) -> None: ...
def clean_fields(self, exclude: Optional[Collection[str]] = ...) -> None: ...
def validate_unique(self, exclude: Optional[Collection[str]] = ...) -> None: ...
def unique_error_message(
self, model_class: Type[_Self], unique_check: Collection[Union[Callable, str]]
) -> ValidationError: ...
def save(
self,
force_insert: bool = ...,
@@ -37,7 +41,7 @@ class Model(metaclass=ModelBase):
using: Optional[str] = ...,
update_fields: Optional[Union[Sequence[str], str]] = ...,
): ...
def refresh_from_db(self: _Self, using: Optional[str] = ..., fields: Optional[List[str]] = ...) -> _Self: ...
def refresh_from_db(self: _Self, using: Optional[str] = ..., fields: Optional[List[str]] = ...) -> None: ...
def get_deferred_fields(self) -> Set[str]: ...
@classmethod
def check(cls, **kwargs: Any) -> List[CheckMessage]: ...

View File

@@ -1,4 +1,4 @@
from typing import Any, Callable, Iterable, Optional, Union
from typing import Any, Callable, Iterable, Optional, Union, Collection, Type
from django.db.models.base import Model
@@ -18,4 +18,11 @@ class ProtectedError(IntegrityError): ...
class Collector:
def __init__(self, using: str) -> None: ...
def collect(
self,
objs: Collection[Optional[Model]],
source: Optional[Type[Model]] = ...,
source_attr: Optional[str] = ...,
**kwargs: Any
) -> None: ...
def can_fast_delete(self, objs: Union[Model, Iterable[Model]], from_field: Optional[Field] = ...) -> bool: ...

View File

@@ -0,0 +1,30 @@
import enum
from typing import Any, List, Tuple
class ChoicesMeta(enum.EnumMeta):
names: List[str] = ...
choices: List[Tuple[Any, str]] = ...
labels: List[str] = ...
values: List[Any] = ...
def __contains__(self, item: Any) -> bool: ...
class Choices(enum.Enum, metaclass=ChoicesMeta):
def __str__(self): ...
# fake
class _IntegerChoicesMeta(ChoicesMeta):
names: List[str] = ...
choices: List[Tuple[int, str]] = ...
labels: List[str] = ...
values: List[int] = ...
class IntegerChoices(int, Choices, metaclass=_IntegerChoicesMeta): ...
# fake
class _TextChoicesMeta(ChoicesMeta):
names: List[str] = ...
choices: List[Tuple[str, str]] = ...
labels: List[str] = ...
values: List[str] = ...
class TextChoices(str, Choices, metaclass=_TextChoicesMeta): ...

View File

@@ -25,7 +25,9 @@ from django.db.models.expressions import Combinable, Col
from django.db.models.query_utils import RegisterLookupMixin
from django.forms import Field as FormField, Widget
from .mixins import NOT_PROVIDED as NOT_PROVIDED
class NOT_PROVIDED: ...
BLANK_CHOICE_DASH: List[Tuple[str, str]] = ...
_Choice = Tuple[Any, Any]
_ChoiceNamedGroup = Tuple[str, Iterable[_Choice]]
@@ -52,6 +54,8 @@ class Field(RegisterLookupMixin, Generic[_ST, _GT]):
auto_created: bool
primary_key: bool
remote_field: Field
is_relation: bool
related_model: Optional[Type[Model]]
max_length: int
model: Type[Model]
name: str
@@ -61,9 +65,10 @@ class Field(RegisterLookupMixin, Generic[_ST, _GT]):
null: bool = ...
editable: bool = ...
empty_strings_allowed: bool = ...
choices: Optional[_FieldChoices] = ...
choices: _FieldChoices = ...
db_column: Optional[str]
column: str
default: Any
error_messages: _ErrorMessagesToOverride
def __init__(
self,
@@ -105,7 +110,8 @@ class Field(RegisterLookupMixin, Generic[_ST, _GT]):
def db_parameters(self, connection: Any) -> Dict[str, str]: ...
def get_prep_value(self, value: Any) -> Any: ...
def get_internal_type(self) -> str: ...
def formfield(self, **kwargs) -> FormField: ...
# TODO: plugin support
def formfield(self, **kwargs) -> Any: ...
def save_form_data(self, instance: Model, data: Any) -> None: ...
def contribute_to_class(self, cls: Type[Model], name: str, private_only: bool = ...) -> None: ...
def to_python(self, value: Any) -> Any: ...
@@ -128,11 +134,12 @@ class Field(RegisterLookupMixin, Generic[_ST, _GT]):
@property
def cached_col(self) -> Col: ...
def value_from_object(self, obj: Model) -> _GT: ...
def get_attname(self) -> str: ...
class IntegerField(Field[_ST, _GT]):
_pyi_private_set_type: Union[float, int, str, Combinable]
_pyi_private_get_type: int
_pyi_lookup_exact_type: int
_pyi_lookup_exact_type: Union[str, int]
class PositiveIntegerRelDbTypeMixin:
def rel_db_type(self, connection: Any): ...
@@ -180,7 +187,7 @@ class DecimalField(Field[_ST, _GT]):
class AutoField(Field[_ST, _GT]):
_pyi_private_set_type: Union[Combinable, int, str]
_pyi_private_get_type: int
_pyi_lookup_exact_type: int
_pyi_lookup_exact_type: Union[str, int]
class CharField(Field[_ST, _GT]):
_pyi_private_set_type: Union[str, int, Combinable]
@@ -357,20 +364,20 @@ class UUIDField(Field[_ST, _GT]):
_pyi_private_get_type: uuid.UUID
class FilePathField(Field[_ST, _GT]):
path: str = ...
match: Optional[Any] = ...
path: Any = ...
match: Optional[str] = ...
recursive: bool = ...
allow_files: bool = ...
allow_folders: bool = ...
def __init__(
self,
verbose_name: Optional[Union[str, bytes]] = ...,
name: Optional[str] = ...,
path: str = ...,
match: Optional[Any] = ...,
path: Union[str, Callable[..., str]] = ...,
match: Optional[str] = ...,
recursive: bool = ...,
allow_files: bool = ...,
allow_folders: bool = ...,
verbose_name: Optional[str] = ...,
name: Optional[str] = ...,
primary_key: bool = ...,
max_length: int = ...,
unique: bool = ...,
@@ -389,7 +396,8 @@ class FilePathField(Field[_ST, _GT]):
error_messages: Optional[_ErrorMessagesToOverride] = ...,
): ...
class BinaryField(Field[_ST, _GT]): ...
class BinaryField(Field[_ST, _GT]):
_pyi_private_get_type: bytes
class DurationField(Field[_ST, _GT]):
_pyi_private_get_type: timedelta

View File

@@ -1,4 +1,5 @@
from typing import Any, Callable, Iterable, List, Optional, Tuple, Type, TypeVar, Union, overload
from pathlib import Path
from typing import Any, Callable, Iterable, Optional, Type, TypeVar, Union, overload
from django.core.files.base import File
from django.core.files.images import ImageFile
@@ -7,8 +8,6 @@ from django.db.models.base import Model
from django.db.models.fields import Field, _FieldChoices, _ValidatorCallable, _ErrorMessagesToOverride
BLANK_CHOICE_DASH: List[Tuple[str, str]] = ...
class FieldFile(File):
instance: Model = ...
field: FileField = ...
@@ -39,11 +38,10 @@ class FileField(Field):
upload_to: Union[str, Callable] = ...
def __init__(
self,
upload_to: Union[str, Callable, Path] = ...,
storage: Optional[Storage] = ...,
verbose_name: Optional[Union[str, bytes]] = ...,
name: Optional[str] = ...,
upload_to: Union[str, Callable] = ...,
storage: Optional[Storage] = ...,
primary_key: bool = ...,
max_length: Optional[int] = ...,
unique: bool = ...,
blank: bool = ...,

View File

@@ -43,9 +43,8 @@ class RelatedField(FieldCacheMixin, Field[_ST, _GT]):
one_to_one: bool = ...
many_to_many: bool = ...
many_to_one: bool = ...
related_model: Type[Model]
opts: Any = ...
@property
def related_model(self) -> Type[Model]: ...
def get_forward_related_filter(self, obj: Model) -> Dict[str, Union[int, UUID]]: ...
def get_reverse_related_filter(self, obj: Model) -> Q: ...
@property

View File

@@ -18,6 +18,7 @@ class ForeignObjectRel(FieldCacheMixin):
concrete: bool = ...
editable: bool = ...
is_relation: bool = ...
related_model: Type[Model]
null: bool = ...
field: RelatedField = ...
model: Union[Type[Model], str] = ...
@@ -44,8 +45,6 @@ class ForeignObjectRel(FieldCacheMixin):
@property
def name(self) -> str: ...
@property
def related_model(self) -> Type[Model]: ...
@property
def remote_field(self) -> RelatedField: ...
@property
def target_field(self) -> AutoField: ...

View File

@@ -14,9 +14,14 @@ from .text import (
Trim as Trim,
Ord as Ord,
Repeat as Repeat,
SHA1 as SHA1,
SHA224 as SHA224,
SHA256 as SHA256,
SHA384 as SHA384,
SHA512 as SHA512,
StrIndex as StrIndex,
Replace as Replace,
Substr as Substr,
Replace as Replace,
Reverse as Reverse,
)
@@ -81,6 +86,7 @@ from .math import (
Power as Power,
Radians as Radians,
Round as Round,
Sign as Sign,
Sin as Sin,
Sqrt as Sqrt,
Tan as Tan,

View File

@@ -23,3 +23,4 @@ class Round(Transform): ...
class Sin(NumericOutputFieldMixin, Transform): ...
class Sqrt(NumericOutputFieldMixin, Transform): ...
class Tan(NumericOutputFieldMixin, Transform): ...
class Sign(Transform): ...

View File

@@ -55,3 +55,11 @@ class Substr(Func):
class Trim(Transform): ...
class Upper(Transform): ...
class Reverse(Transform): ...
class MySQLSHA2Mixin: ...
class OracleHashMixin: ...
class PostgreSQLSHAMixin: ...
class SHA1(OracleHashMixin, PostgreSQLSHAMixin, Transform): ...
class SHA224(MySQLSHA2Mixin, PostgreSQLSHAMixin, Transform): ...
class SHA256(MySQLSHA2Mixin, OracleHashMixin, PostgreSQLSHAMixin, Transform): ...
class SHA384(MySQLSHA2Mixin, OracleHashMixin, PostgreSQLSHAMixin, Transform): ...
class SHA512(MySQLSHA2Mixin, OracleHashMixin, PostgreSQLSHAMixin, Transform): ...

View File

@@ -31,11 +31,9 @@ class Lookup(Generic[_T]):
def process_lhs(
self, compiler: SQLCompiler, connection: DatabaseWrapper, lhs: Optional[Expression] = ...
) -> Tuple[str, List[Union[int, str]]]: ...
def process_rhs(
self, compiler: SQLCompiler, connection: DatabaseWrapper
) -> Tuple[str, Union[List[Union[int, str]], Tuple[int, int]]]: ...
def process_rhs(self, compiler: SQLCompiler, connection: DatabaseWrapper) -> Tuple[str, List[Union[int, str]]]: ...
def rhs_is_direct_value(self) -> bool: ...
def relabeled_clone(self, relabels: Mapping[str, str]) -> Union[BuiltinLookup, FieldGetDbPrepValueMixin]: ...
def relabeled_clone(self: _T, relabels: Mapping[str, str]) -> _T: ...
def get_group_by_cols(self) -> List[Expression]: ...
def as_sql(self, compiler: Any, connection: Any) -> Any: ...
def contains_aggregate(self) -> bool: ...

View File

@@ -1,16 +1,17 @@
from typing import Any, Dict, List, Optional, Tuple, Type, TypeVar
from typing import Any, Dict, List, Optional, Tuple, Type, TypeVar, Iterable, Union
from django.db.models.base import Model
from django.db.models.query import QuerySet
_T = TypeVar("_T", bound=Model, covariant=True)
_M = TypeVar("_M", bound="BaseManager")
class BaseManager(QuerySet[_T]):
creation_counter: int = ...
auto_created: bool = ...
use_in_migrations: bool = ...
name: str = ...
model: Type[Model] = ...
model: Type[_T] = ...
db: str
def __init__(self) -> None: ...
def deconstruct(self) -> Tuple[bool, str, None, Tuple, Dict[str, int]]: ...
@@ -20,13 +21,18 @@ class BaseManager(QuerySet[_T]):
@classmethod
def _get_queryset_methods(cls, queryset_class: type) -> Dict[str, Any]: ...
def contribute_to_class(self, model: Type[Model], name: str) -> None: ...
def db_manager(self, using: Optional[str] = ..., hints: Optional[Dict[str, Model]] = ...) -> Manager: ...
def db_manager(self: _M, using: Optional[str] = ..., hints: Optional[Dict[str, Model]] = ...) -> _M: ...
def get_queryset(self) -> QuerySet[_T]: ...
class Manager(BaseManager[_T]): ...
class RelatedManager(Manager[_T]):
def add(self, *objs: Model, bulk: bool = ...) -> None: ...
related_val: Tuple[int, ...]
def add(self, *objs: Union[_T, int], bulk: bool = ...) -> None: ...
def remove(self, *objs: Union[_T, int], bulk: bool = ...) -> None: ...
def set(
self, objs: Union[QuerySet[_T], Iterable[Union[_T, int]]], *, bulk: bool = ..., clear: bool = ...
) -> None: ...
def clear(self) -> None: ...
class ManagerDescriptor:

View File

@@ -1,5 +1,5 @@
import collections
from typing import Any, Callable, Dict, Generic, Iterator, List, Optional, Sequence, Set, Tuple, Type, TypeVar, Union
from typing import Any, Callable, Dict, Generic, Iterator, List, Optional, Sequence, Tuple, Type, TypeVar, Union
from django.apps.config import AppConfig
from django.apps.registry import Apps
@@ -8,6 +8,7 @@ from django.contrib.postgres.fields.array import ArrayField
from django.contrib.postgres.fields.citext import CIText
from django.db.backends.sqlite3.base import DatabaseWrapper
from django.db.models.base import Model
from django.db.models.constraints import BaseConstraint
from django.db.models.fields.mixins import FieldCacheMixin
from django.db.models.fields.related import ManyToManyField, OneToOneField
from django.db.models.fields.reverse_related import ForeignObjectRel
@@ -23,8 +24,8 @@ IMMUTABLE_WARNING: str
DEFAULT_NAMES: Tuple[str, ...]
def normalize_together(
option_together: Any
) -> Union[List[Union[Tuple[str, str], int]], Set[Tuple[str, str]], Tuple, int, str]: ...
option_together: Union[Sequence[Tuple[str, str]], Tuple[str, str]]
) -> Tuple[Tuple[str, str], ...]: ...
def make_immutable_fields_list(
name: str, data: Union[Iterator[Any], List[Union[ArrayField, CIText]], List[Union[Field, FieldCacheMixin]]]
) -> ImmutableList: ...
@@ -34,6 +35,7 @@ _M = TypeVar("_M", bound=Model)
class Options(Generic[_M]):
base_manager: Manager
concrete_fields: ImmutableList
constraints: List[BaseConstraint]
default_manager: Manager
fields: ImmutableList
local_concrete_fields: ImmutableList
@@ -108,6 +110,8 @@ class Options(Generic[_M]):
def managers(self) -> List[Manager]: ...
@property
def managers_map(self) -> Dict[str, Manager]: ...
@property
def db_returning_fields(self) -> List[Field]: ...
def get_field(self, field_name: Union[Callable, str]) -> Field: ...
def get_base_chain(self, model: Type[Model]) -> List[Type[Model]]: ...
def get_parent_list(self) -> List[Type[Model]]: ...

View File

@@ -16,6 +16,7 @@ from typing import (
TypeVar,
Union,
overload,
Reversible,
)
from django.db.models.base import Model
@@ -52,8 +53,9 @@ class _BaseQuerySet(Generic[_T], Sized):
def get(self, *args: Any, **kwargs: Any) -> _T: ...
def create(self, *args: Any, **kwargs: Any) -> _T: ...
def bulk_create(
self, objs: Iterable[Model], batch_size: Optional[int] = ..., ignore_conflicts: bool = ...
self, objs: Iterable[_T], batch_size: Optional[int] = ..., ignore_conflicts: bool = ...
) -> List[_T]: ...
def bulk_update(self, objs: Iterable[_T], fields: Sequence[str], batch_size: Optional[int] = ...) -> None: ...
def get_or_create(self, defaults: Optional[MutableMapping[str, Any]] = ..., **kwargs: Any) -> Tuple[_T, bool]: ...
def update_or_create(
self, defaults: Optional[MutableMapping[str, Any]] = ..., **kwargs: Any
@@ -119,16 +121,15 @@ class _BaseQuerySet(Generic[_T], Sized):
@property
def db(self) -> str: ...
def resolve_expression(self, *args: Any, **kwargs: Any) -> Any: ...
# TODO: remove when django adds __class_getitem__ methods
def __getattr__(self, item: str) -> Any: ...
class QuerySet(_BaseQuerySet[_T], Collection[_T], Sized):
class QuerySet(_BaseQuerySet[_T], Collection[_T], Reversible[_T], Sized):
def __iter__(self) -> Iterator[_T]: ...
def __contains__(self, x: object) -> bool: ...
@overload
def __getitem__(self, i: int) -> _T: ...
@overload
def __getitem__(self: _QS, s: slice) -> _QS: ...
def __reversed__(self) -> Iterator[_T]: ...
_Row = TypeVar("_Row", covariant=True)

View File

@@ -1,13 +1,13 @@
from collections import OrderedDict, namedtuple
from typing import Any, Dict, Iterator, List, Optional, Set, Tuple, Type, Union
from collections import namedtuple
from typing import Any, Collection, Dict, Iterator, List, Mapping, Optional, Sequence, Set, Tuple, Type
from django.db.models.base import Model
from django.db.models.expressions import Expression
from django.db.models.fields import Field
from django.db.models.fields.mixins import FieldCacheMixin
from django.db.models.sql.compiler import SQLCompiler
from django.db.models.sql.query import Query
from django.db.models.sql.where import WhereNode
from django.db.models.fields import Field
from django.utils import tree
PathInfo = namedtuple("PathInfo", "from_opts to_opts target_fields join_field m2m direct filtered_relation")
@@ -23,12 +23,8 @@ class QueryWrapper:
def as_sql(self, compiler: SQLCompiler = ..., connection: Any = ...) -> Any: ...
class Q(tree.Node):
children: Union[List[Dict[str, str]], List[Tuple[str, Any]], List[Q]]
connector: str
negated: bool
AND: str = ...
OR: str = ...
default: Any = ...
conditional: bool = ...
def __init__(self, *args: Any, **kwargs: Any) -> None: ...
def __or__(self, other: Any) -> Q: ...
@@ -46,8 +42,8 @@ class Q(tree.Node):
class DeferredAttribute:
field_name: str = ...
field: Field
def __init__(self, field_name: str) -> None: ...
def __get__(self, instance: Optional[Model], cls: Type[Model] = ...) -> Any: ...
class RegisterLookupMixin:
lookup_name: str
@@ -65,15 +61,11 @@ class RegisterLookupMixin:
def select_related_descend(
field: Field,
restricted: bool,
requested: Optional[
Union[Dict[str, Dict[str, Dict[str, Dict[str, Dict[str, Dict[str, Dict[str, Dict[Any, Any]]]]]]]], bool]
],
load_fields: Optional[Set[str]],
requested: Optional[Mapping[str, Any]],
load_fields: Optional[Collection[str]],
reverse: bool = ...,
) -> bool: ...
def refs_expression(
lookup_parts: List[str], annotations: OrderedDict
) -> Union[Tuple[bool, Tuple], Tuple[Expression, List[str]]]: ...
def refs_expression(lookup_parts: Sequence[str], annotations: Mapping[str, bool]) -> Tuple[bool, Sequence[str]]: ...
def check_rel_lookup_compatibility(model: Type[Model], target_opts: Any, field: FieldCacheMixin) -> bool: ...
class FilteredRelation:

View File

@@ -25,7 +25,7 @@ class SQLCompiler:
def setup_query(self) -> None: ...
has_extra_select: Any = ...
def pre_sql_setup(
self
self,
) -> Tuple[
List[Tuple[Expression, Tuple[str, Union[List[Any], Tuple[str, str]]], None]],
List[Tuple[Expression, Tuple[str, List[Union[int, str]], bool]]],
@@ -40,7 +40,7 @@ class SQLCompiler:
self, expressions: List[Expression], having: Union[List[Expression], Tuple]
) -> List[Expression]: ...
def get_select(
self
self,
) -> Tuple[
List[Tuple[Expression, Tuple[str, List[Union[int, str]]], Optional[str]]],
Optional[Dict[str, Any]],

View File

@@ -1,6 +1,6 @@
import collections
from collections import OrderedDict, namedtuple
from typing import Any, Callable, Dict, Iterator, List, Optional, Sequence, Set, Tuple, Type, Union
from typing import Any, Callable, Dict, Iterator, List, Optional, Sequence, Set, Tuple, Type, Union, Iterable
from django.db.models.lookups import Lookup, Transform
from django.db.models.query_utils import PathInfo, RegisterLookupMixin
@@ -155,19 +155,19 @@ class Query:
def add_ordering(self, *ordering: Any) -> None: ...
def clear_ordering(self, force_empty: bool) -> None: ...
def set_group_by(self) -> None: ...
def add_select_related(self, fields: Tuple[str]) -> None: ...
def add_select_related(self, fields: Iterable[str]) -> None: ...
def add_extra(
self,
select: Optional[Union[Dict[str, int], Dict[str, str], OrderedDict]],
select_params: Optional[Union[List[int], List[str], Tuple[int]]],
where: Optional[List[str]],
params: Optional[List[str]],
tables: Optional[List[str]],
order_by: Optional[Union[List[str], Tuple[str]]],
select: Optional[Dict[str, Any]],
select_params: Optional[Iterable[Any]],
where: Optional[Sequence[str]],
params: Optional[Sequence[str]],
tables: Optional[Sequence[str]],
order_by: Optional[Sequence[str]],
) -> None: ...
def clear_deferred_loading(self) -> None: ...
def add_deferred_loading(self, field_names: Tuple[str]) -> None: ...
def add_immediate_loading(self, field_names: Tuple[str]) -> None: ...
def add_deferred_loading(self, field_names: Iterable[str]) -> None: ...
def add_immediate_loading(self, field_names: Iterable[str]) -> None: ...
def get_loaded_field_names(self) -> Dict[Type[Model], Set[str]]: ...
def get_loaded_field_names_cb(
self, target: Dict[Type[Model], Set[str]], model: Type[Model], fields: Set[Field]

View File

@@ -2,6 +2,8 @@ from django.core.exceptions import ValidationError as ValidationError
from .forms import Form as Form, BaseForm as BaseForm
from .formsets import BaseFormSet as BaseFormSet, all_valid as all_valid, formset_factory as formset_factory
from .models import (
ModelForm as ModelForm,
ModelChoiceField as ModelChoiceField,

View File

@@ -68,4 +68,6 @@ class BaseForm:
def visible_fields(self): ...
def get_initial_for_field(self, field: Field, field_name: str) -> Any: ...
class Form(BaseForm): ...
class Form(BaseForm):
base_fields: Dict[str, Field]
declared_fields: Dict[str, Field]

View File

@@ -18,6 +18,7 @@ from typing import (
from django.contrib.auth.base_user import AbstractBaseUser
from django.contrib.sessions.backends.base import SessionBase
from django.contrib.sites.models import Site
from django.utils.datastructures import CaseInsensitiveMapping, ImmutableList, MultiValueDict
from django.core.files import uploadedfile, uploadhandler
@@ -51,6 +52,7 @@ class HttpRequest(BytesIO):
content_type: Optional[str] = ...
content_params: Optional[Dict[str, str]] = ...
user: AbstractBaseUser
site: Site
session: SessionBase
encoding: Optional[str] = ...
upload_handlers: UploadHandlerList = ...
@@ -63,7 +65,7 @@ class HttpRequest(BytesIO):
self, key: str, default: Any = ..., salt: str = ..., max_age: Optional[int] = ...
) -> Optional[str]: ...
def get_raw_uri(self) -> str: ...
def build_absolute_uri(self, location: str = ...) -> str: ...
def build_absolute_uri(self, location: Optional[str] = ...) -> str: ...
@property
def scheme(self) -> Optional[str]: ...
def is_secure(self) -> bool: ...

View File

@@ -63,7 +63,8 @@ class HttpResponseBase(Iterable[Any]):
class HttpResponse(HttpResponseBase):
client: Client
context: Optional[Context]
context: Context
content: Any
csrf_cookie_set: bool
redirect_chain: List[Tuple[str, int]]
request: Dict[str, Any]
@@ -78,10 +79,6 @@ class HttpResponse(HttpResponseBase):
def __init__(self, content: object = ..., *args: Any, **kwargs: Any) -> None: ...
def serialize(self) -> bytes: ...
@property
def content(self) -> Any: ...
@content.setter
def content(self, value: Any) -> None: ...
@property
def url(self) -> str: ...
def json(self) -> Dict[str, Any]: ...
@@ -107,7 +104,7 @@ class FileResponse(StreamingHttpResponse):
def json(self) -> Dict[str, Any]: ...
class HttpResponseRedirectBase(HttpResponse):
allowed_schemes = ... # type: List[str]
allowed_schemes: List[str] = ...
def __init__(self, redirect_to: str, *args: Any, **kwargs: Any) -> None: ...
class HttpResponseRedirect(HttpResponseRedirectBase): ...

View File

@@ -1,7 +1,11 @@
from typing import Any, Callable, Dict, List, Optional, Protocol, Sequence, Type, TypeVar, Union
from django.db.models.base import Model
from django.http.response import HttpResponse as HttpResponse, HttpResponseRedirect as HttpResponseRedirect
from django.http.response import (
HttpResponse as HttpResponse,
HttpResponseRedirect as HttpResponseRedirect,
HttpResponsePermanentRedirect as HttpResponsePermanentRedirect,
)
from django.db.models import Manager, QuerySet
from django.http import HttpRequest
@@ -26,7 +30,7 @@ class SupportsGetAbsoluteUrl(Protocol): ...
def redirect(
to: Union[Callable, str, SupportsGetAbsoluteUrl], *args: Any, permanent: bool = ..., **kwargs: Any
) -> HttpResponseRedirect: ...
) -> Union[HttpResponseRedirect, HttpResponsePermanentRedirect]: ...
_T = TypeVar("_T", bound=Model)

View File

@@ -1,7 +1,7 @@
from io import BytesIO
from typing import Any, Dict, List, Optional, Pattern, Tuple, Type, Union
from django.contrib.auth.models import User
from django.contrib.auth.models import AbstractUser
from django.contrib.sessions.backends.base import SessionBase
from django.core.handlers.base import BaseHandler
from django.core.serializers.json import DjangoJSONEncoder
@@ -9,6 +9,8 @@ from django.http.cookie import SimpleCookie
from django.http.request import HttpRequest
from django.http.response import HttpResponse, HttpResponseBase
from django.core.handlers.wsgi import WSGIRequest
BOUNDARY: str = ...
MULTIPART_CONTENT: str = ...
CONTENT_TYPE_RE: Pattern = ...
@@ -40,13 +42,13 @@ class RequestFactory:
cookies: SimpleCookie = ...
errors: BytesIO = ...
def __init__(self, *, json_encoder: Any = ..., **defaults: Any) -> None: ...
def request(self, **request: Any) -> HttpRequest: ...
def get(self, path: str, data: Any = ..., secure: bool = ..., **extra: Any) -> HttpRequest: ...
def request(self, **request: Any) -> WSGIRequest: ...
def get(self, path: str, data: Any = ..., secure: bool = ..., **extra: Any) -> WSGIRequest: ...
def post(
self, path: str, data: Any = ..., content_type: str = ..., secure: bool = ..., **extra: Any
) -> HttpRequest: ...
def head(self, path: str, data: Any = ..., secure: bool = ..., **extra: Any) -> HttpRequest: ...
def trace(self, path: str, secure: bool = ..., **extra: Any) -> HttpRequest: ...
) -> WSGIRequest: ...
def head(self, path: str, data: Any = ..., secure: bool = ..., **extra: Any) -> WSGIRequest: ...
def trace(self, path: str, secure: bool = ..., **extra: Any) -> WSGIRequest: ...
def options(
self,
path: str,
@@ -54,16 +56,16 @@ class RequestFactory:
content_type: str = ...,
secure: bool = ...,
**extra: Any
) -> HttpRequest: ...
) -> WSGIRequest: ...
def put(
self, path: str, data: Any = ..., content_type: str = ..., secure: bool = ..., **extra: Any
) -> HttpRequest: ...
) -> WSGIRequest: ...
def patch(
self, path: str, data: Any = ..., content_type: str = ..., secure: bool = ..., **extra: Any
) -> HttpRequest: ...
) -> WSGIRequest: ...
def delete(
self, path: str, data: Any = ..., content_type: str = ..., secure: bool = ..., **extra: Any
) -> HttpRequest: ...
) -> WSGIRequest: ...
def generic(
self,
method: str,
@@ -72,7 +74,7 @@ class RequestFactory:
content_type: Optional[str] = ...,
secure: bool = ...,
**extra: Any
) -> HttpRequest: ...
) -> WSGIRequest: ...
class Client:
json_encoder: Type[DjangoJSONEncoder] = ...
@@ -82,13 +84,11 @@ class Client:
handler: ClientHandler = ...
exc_info: None = ...
def __init__(self, enforce_csrf_checks: bool = ..., **defaults: Any) -> None: ...
def request(self, **request: Any) -> HttpResponse: ...
def get(self, path: str, data: Any = ..., secure: bool = ..., **extra: Any) -> HttpResponse: ...
def post(
self, path: str, data: Any = ..., content_type: str = ..., secure: bool = ..., **extra: Any
) -> HttpResponse: ...
def head(self, path: str, data: Any = ..., secure: bool = ..., **extra: Any) -> HttpResponse: ...
def trace(self, path: str, secure: bool = ..., **extra: Any) -> HttpResponse: ...
def request(self, **request: Any) -> Any: ...
def get(self, path: str, data: Any = ..., secure: bool = ..., **extra: Any) -> Any: ...
def post(self, path: str, data: Any = ..., content_type: str = ..., secure: bool = ..., **extra: Any) -> Any: ...
def head(self, path: str, data: Any = ..., secure: bool = ..., **extra: Any) -> Any: ...
def trace(self, path: str, secure: bool = ..., **extra: Any) -> Any: ...
def options(
self,
path: str,
@@ -96,16 +96,10 @@ class Client:
content_type: str = ...,
secure: bool = ...,
**extra: Any
) -> HttpResponse: ...
def put(
self, path: str, data: Any = ..., content_type: str = ..., secure: bool = ..., **extra: Any
) -> HttpResponse: ...
def patch(
self, path: str, data: Any = ..., content_type: str = ..., secure: bool = ..., **extra: Any
) -> HttpResponse: ...
def delete(
self, path: str, data: Any = ..., content_type: str = ..., secure: bool = ..., **extra: Any
) -> HttpResponse: ...
) -> Any: ...
def put(self, path: str, data: Any = ..., content_type: str = ..., secure: bool = ..., **extra: Any) -> Any: ...
def patch(self, path: str, data: Any = ..., content_type: str = ..., secure: bool = ..., **extra: Any) -> Any: ...
def delete(self, path: str, data: Any = ..., content_type: str = ..., secure: bool = ..., **extra: Any) -> Any: ...
def generic(
self,
method: str,
@@ -114,12 +108,12 @@ class Client:
content_type: Optional[str] = ...,
secure: bool = ...,
**extra: Any
) -> HttpResponse: ...
) -> Any: ...
def store_exc_info(self, **kwargs: Any) -> None: ...
@property
def session(self) -> SessionBase: ...
def login(self, **credentials: Any) -> bool: ...
def force_login(self, user: User, backend: Optional[str] = ...) -> None: ...
def force_login(self, user: AbstractUser, backend: Optional[str] = ...) -> None: ...
def logout(self) -> None: ...
def conditional_content_removal(request: HttpRequest, response: HttpResponseBase) -> HttpResponse: ...

View File

@@ -1,7 +1,7 @@
import threading
import unittest
from datetime import date
from typing import Any, Callable, Dict, Iterator, List, Optional, Set, Tuple, Type, Union
from typing import Any, Callable, Dict, Iterator, List, Optional, Set, Tuple, Type, Union, ClassVar, overload
from django.core.exceptions import ImproperlyConfigured
from django.core.handlers.wsgi import WSGIHandler
@@ -47,7 +47,8 @@ class SimpleTestCase(unittest.TestCase):
client_class: Any = ...
client: Client
allow_database_queries: bool = ...
databases: Set[str] = ...
# TODO: str -> Literal['__all__']
databases: Union[Set[str], str] = ...
def __call__(self, result: Optional[unittest.TestResult] = ...) -> None: ...
def settings(self, **kwargs: Any) -> Any: ...
def modify_settings(self, **kwargs: Any) -> Any: ...
@@ -145,9 +146,14 @@ class TransactionTestCase(SimpleTestCase):
ordered: bool = ...,
msg: Optional[str] = ...,
) -> None: ...
@overload
def assertNumQueries(
self, num: int, func: Optional[Union[Callable, Type[list]]] = ..., *args: Any, using: Any = ..., **kwargs: Any
) -> Optional[_AssertNumQueriesContext]: ...
self, num: int, func: Callable[..., Any], *args: Any, using: str = ..., **kwargs: Any
) -> None: ...
@overload
def assertNumQueries(
self, num: int, func: None = ..., *args: Any, using: str = ..., **kwargs: Any
) -> _AssertNumQueriesContext: ...
class TestCase(TransactionTestCase):
@classmethod
@@ -198,13 +204,12 @@ class LiveServerThread(threading.Thread):
def terminate(self) -> None: ...
class LiveServerTestCase(TransactionTestCase):
live_server_url: ClassVar[str]
host: str = ...
port: int = ...
server_thread_class: Type[Any] = ...
server_thread: Any
static_handler: Any = ...
@classmethod
def live_server_url(cls): ...
class SerializeMixin:
lockfile: Any = ...

View File

@@ -1,9 +1,22 @@
import decimal
import warnings
from contextlib import contextmanager
from decimal import Decimal
from io import StringIO
from typing import Any, Callable, Dict, Iterable, Iterator, List, Mapping, Optional, Set, Tuple, Type, Union
from typing import (
Any,
Callable,
Dict,
Iterable,
Iterator,
List,
Mapping,
Optional,
Set,
Tuple,
Type,
Union,
ContextManager,
)
from django.apps.registry import Apps
from django.core.checks.registry import CheckRegistry
@@ -86,7 +99,7 @@ class ignore_warnings(TestContextDecorator):
ignore_kwargs: Dict[str, Any] = ...
filter_func: Callable = ...
def __init__(self, **kwargs: Any) -> None: ...
catch_warnings: warnings.catch_warnings = ...
catch_warnings: ContextManager[Optional[list]] = ...
requires_tz_support: Any

View File

@@ -1,4 +1,5 @@
from os.path import abspath
from pathlib import Path
from typing import Any, Union
abspathu = abspath
@@ -7,3 +8,4 @@ def upath(path: Any): ...
def npath(path: Any): ...
def safe_join(base: Union[bytes, str], *paths: Any) -> str: ...
def symlinks_supported() -> Any: ...
def to_path(value: Union[Path, str]) -> Path: ...

View File

@@ -5,6 +5,7 @@ from django.http.response import HttpResponse
class RemovedInDjango30Warning(PendingDeprecationWarning): ...
class RemovedInDjango31Warning(PendingDeprecationWarning): ...
class RemovedInDjango40Warning(PendingDeprecationWarning): ...
class RemovedInNextVersionWarning(DeprecationWarning): ...
class warn_about_renamed_method:

View File

@@ -1,25 +1,59 @@
from typing import Any, Optional
import datetime
from decimal import Decimal
from typing import Any, TypeVar, overload, Union
from django.utils.functional import Promise
from typing_extensions import Literal
class DjangoUnicodeDecodeError(UnicodeDecodeError):
obj: bytes = ...
def __init__(self, obj: bytes, *args: Any) -> None: ...
python_2_unicode_compatible: Any
_P = TypeVar("_P", bound=Promise)
_S = TypeVar("_S", bound=str)
_PT = TypeVar("_PT", None, int, float, Decimal, datetime.datetime, datetime.date, datetime.time)
@overload
def smart_text(s: _P, encoding: str = ..., strings_only: bool = ..., errors: str = ...) -> _P: ...
@overload
def smart_text(s: _PT, encoding: str = ..., strings_only: Literal[True] = ..., errors: str = ...) -> _PT: ...
@overload
def smart_text(s: _S, encoding: str = ..., strings_only: bool = ..., errors: str = ...) -> _S: ...
@overload
def smart_text(s: Any, encoding: str = ..., strings_only: bool = ..., errors: str = ...) -> str: ...
def is_protected_type(obj: Any) -> bool: ...
def force_text(s: Any, encoding: str = ..., strings_only: bool = ..., errors: str = ...) -> Optional[str]: ...
@overload
def force_text(s: _PT, encoding: str = ..., strings_only: Literal[True] = ..., errors: str = ...) -> _PT: ...
@overload
def force_text(s: _S, encoding: str = ..., strings_only: bool = ..., errors: str = ...) -> _S: ...
@overload
def force_text(s: Any, encoding: str = ..., strings_only: bool = ..., errors: str = ...) -> str: ...
@overload
def smart_bytes(s: _P, encoding: str = ..., strings_only: bool = ..., errors: str = ...) -> _P: ...
@overload
def smart_bytes(s: _PT, encoding: str = ..., strings_only: Literal[True] = ..., errors: str = ...) -> _PT: ...
@overload
def smart_bytes(s: Any, encoding: str = ..., strings_only: bool = ..., errors: str = ...) -> bytes: ...
@overload
def force_bytes(s: _PT, encoding: str = ..., strings_only: Literal[True] = ..., errors: str = ...) -> _PT: ...
@overload
def force_bytes(s: Any, encoding: str = ..., strings_only: bool = ..., errors: str = ...) -> bytes: ...
smart_str = smart_text
force_str = force_text
def iri_to_uri(iri: Optional[str]) -> Optional[str]: ...
def uri_to_iri(uri: Optional[str]) -> Optional[str]: ...
@overload
def iri_to_uri(iri: None) -> None: ...
@overload
def iri_to_uri(iri: Union[str, Promise]) -> str: ...
@overload
def uri_to_iri(iri: None) -> None: ...
@overload
def uri_to_iri(iri: str) -> str: ...
def escape_uri_path(path: str) -> str: ...
def repercent_broken_unicode(path: bytes) -> bytes: ...
def filepath_to_uri(path: Optional[str]) -> Optional[str]: ...
@overload
def filepath_to_uri(path: None) -> None: ...
@overload
def filepath_to_uri(path: str) -> str: ...
def get_system_encoding() -> str: ...
DEFAULT_LOCALE_ENCODING: Any

View File

@@ -25,6 +25,9 @@ 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 url_has_allowed_host_and_scheme(
url: Optional[str], allowed_hosts: Optional[Union[str, Iterable[str]]], require_https: bool = ...
) -> bool: ...
def is_safe_url(
url: Optional[str], allowed_hosts: Optional[Union[str, Iterable[str]]], require_https: bool = ...
) -> bool: ...

View File

@@ -1,8 +1,10 @@
import types
from contextlib import ContextDecorator
from datetime import date, datetime as datetime, time, timedelta as timedelta, tzinfo as tzinfo
from datetime import date, datetime as datetime, time, timedelta as timedelta, tzinfo as tzinfo, timezone
from typing import Optional, Union, Type
from pytz import BaseTzInfo
_AnyTime = Union[time, datetime]
class UTC(tzinfo):
@@ -30,10 +32,14 @@ class LocalTimezone(ReferenceLocalTimezone):
utc: UTC = ...
def get_fixed_timezone(offset: Union[timedelta, int]) -> tzinfo: ...
def get_default_timezone() -> tzinfo: ...
def get_fixed_timezone(offset: Union[timedelta, int]) -> timezone: ...
def get_default_timezone() -> BaseTzInfo: ...
def get_default_timezone_name() -> str: ...
def get_current_timezone() -> tzinfo: ...
# Strictly speaking, it is possible to activate() a non-pytz timezone,
# in which case BaseTzInfo is incorrect. However, this is unlikely,
# so we use it anyway, to keep things ergonomic for most users.
def get_current_timezone() -> BaseTzInfo: ...
def get_current_timezone_name() -> str: ...
def activate(timezone: Union[tzinfo, str]) -> None: ...
def deactivate() -> None: ...

View File

@@ -1,16 +1,16 @@
from typing import Any, Dict, Iterable, Optional, Tuple, Union, Sequence
from typing import Any, Dict, Iterable, Optional, Tuple, Union, Sequence, List
from django.db.models.sql.where import NothingNode
_NodeChildren = Iterable[Union["Node", NothingNode, Sequence[Any]]]
class Node:
default: str = ...
children: List[Any]
default: Any = ...
connector: str = ...
negated: bool = ...
def __init__(
self,
children: Optional[Iterable[Union[Node, NothingNode, Sequence[Any]]]] = ...,
connector: Optional[str] = ...,
negated: bool = ...,
self, children: Optional[_NodeChildren] = ..., connector: Optional[str] = ..., negated: bool = ...
) -> None: ...
def __deepcopy__(self, memodict: Dict[Any, Any]) -> Node: ...
def __len__(self) -> int: ...

View File

@@ -63,4 +63,4 @@ class ExceptionReporter:
): ...
def technical_404_response(request: HttpRequest, exception: Http404) -> HttpResponse: ...
def default_urlconf(request: HttpRequest) -> HttpResponse: ...
def default_urlconf(request: Optional[HttpResponse]) -> HttpResponse: ...

View File

@@ -13,6 +13,7 @@ class View:
def __init__(self, **kwargs: Any) -> None: ...
@classmethod
def as_view(cls: Any, **initkwargs: Any) -> Callable[..., http.HttpResponse]: ...
def setup(self, request: http.HttpRequest, *args: Any, **kwargs: Any) -> None: ...
def dispatch(self, request: http.HttpRequest, *args: Any, **kwargs: Any) -> http.HttpResponse: ...
def http_method_not_allowed(self, request: http.HttpRequest, *args: Any, **kwargs: Any) -> http.HttpResponse: ...
def options(self, request: http.HttpRequest, *args: Any, **kwargs: Any) -> http.HttpResponse: ...

View File

@@ -1,4 +1,5 @@
import os
import sys
from collections import defaultdict
from contextlib import contextmanager
from typing import (
@@ -48,6 +49,9 @@ def initialize_django(settings_module: str) -> Tuple['Apps', 'LazySettings']:
with temp_environ():
os.environ['DJANGO_SETTINGS_MODULE'] = settings_module
# add current directory to sys.path
sys.path.append(os.getcwd())
def noop_class_getitem(cls, key):
return cls
@@ -73,176 +77,12 @@ def initialize_django(settings_module: str) -> Tuple['Apps', 'LazySettings']:
return apps, settings
class DjangoFieldsContext:
def __init__(self, django_context: 'DjangoContext') -> None:
self.django_context = django_context
def get_attname(self, field: Field) -> str:
attname = field.attname
return attname
def get_field_nullability(self, field: Field, method: Optional[str]) -> bool:
nullable = field.null
if not nullable and isinstance(field, CharField) and field.blank:
return True
if method == '__init__':
if field.primary_key or isinstance(field, ForeignKey):
return True
if method == 'create':
if isinstance(field, AutoField):
return True
if field.has_default():
return True
return nullable
def get_field_set_type(self, api: TypeChecker, field: Field, *, method: str) -> MypyType:
""" Get a type of __set__ for this specific Django field. """
target_field = field
if isinstance(field, ForeignKey):
target_field = field.target_field
field_info = helpers.lookup_class_typeinfo(api, target_field.__class__)
if field_info is None:
return AnyType(TypeOfAny.from_error)
field_set_type = helpers.get_private_descriptor_type(field_info, '_pyi_private_set_type',
is_nullable=self.get_field_nullability(field, method))
if isinstance(target_field, ArrayField):
argument_field_type = self.get_field_set_type(api, target_field.base_field, method=method)
field_set_type = helpers.convert_any_to_type(field_set_type, argument_field_type)
return field_set_type
def get_field_get_type(self, api: TypeChecker, field: Field, *, method: str) -> MypyType:
""" Get a type of __get__ for this specific Django field. """
field_info = helpers.lookup_class_typeinfo(api, field.__class__)
if field_info is None:
return AnyType(TypeOfAny.unannotated)
is_nullable = self.get_field_nullability(field, method)
if isinstance(field, RelatedField):
related_model_cls = self.django_context.fields_context.get_related_model_cls(field)
if method == 'values':
primary_key_field = self.django_context.get_primary_key_field(related_model_cls)
return self.get_field_get_type(api, primary_key_field, method=method)
model_info = helpers.lookup_class_typeinfo(api, related_model_cls)
if model_info is None:
return AnyType(TypeOfAny.unannotated)
return Instance(model_info, [])
else:
return helpers.get_private_descriptor_type(field_info, '_pyi_private_get_type',
is_nullable=is_nullable)
def get_related_model_cls(self, field: Union[RelatedField, ForeignObjectRel]) -> Type[Model]:
if isinstance(field, RelatedField):
related_model_cls = field.remote_field.model
else:
related_model_cls = field.field.model
if isinstance(related_model_cls, str):
if related_model_cls == 'self':
# same model
related_model_cls = field.model
elif '.' not in related_model_cls:
# same file model
related_model_fullname = field.model.__module__ + '.' + related_model_cls
related_model_cls = self.django_context.get_model_class_by_fullname(related_model_fullname)
else:
related_model_cls = self.django_context.apps_registry.get_model(related_model_cls)
return related_model_cls
class LookupsAreUnsupported(Exception):
pass
class DjangoLookupsContext:
def __init__(self, django_context: 'DjangoContext'):
self.django_context = django_context
def _resolve_field_from_parts(self, field_parts: Iterable[str], model_cls: Type[Model]) -> Field:
currently_observed_model = model_cls
field = None
for field_part in field_parts:
if field_part == 'pk':
field = self.django_context.get_primary_key_field(currently_observed_model)
continue
field = currently_observed_model._meta.get_field(field_part)
if isinstance(field, RelatedField):
currently_observed_model = field.related_model
model_name = currently_observed_model._meta.model_name
if (model_name is not None
and field_part == (model_name + '_id')):
field = self.django_context.get_primary_key_field(currently_observed_model)
if isinstance(field, ForeignObjectRel):
currently_observed_model = field.related_model
assert field is not None
return field
def resolve_lookup_info_field(self, model_cls: Type[Model], lookup: str) -> Field:
query = Query(model_cls)
lookup_parts, field_parts, is_expression = query.solve_lookup_type(lookup)
if lookup_parts:
raise LookupsAreUnsupported()
return self._resolve_field_from_parts(field_parts, model_cls)
def resolve_lookup_expected_type(self, ctx: MethodContext, model_cls: Type[Model], lookup: str) -> MypyType:
query = Query(model_cls)
try:
lookup_parts, field_parts, is_expression = query.solve_lookup_type(lookup)
if is_expression:
return AnyType(TypeOfAny.explicit)
except FieldError as exc:
ctx.api.fail(exc.args[0], ctx.context)
return AnyType(TypeOfAny.from_error)
field = self._resolve_field_from_parts(field_parts, model_cls)
lookup_cls = None
if lookup_parts:
lookup = lookup_parts[-1]
lookup_cls = field.get_lookup(lookup)
if lookup_cls is None:
# unknown lookup
return AnyType(TypeOfAny.explicit)
if lookup_cls is None or isinstance(lookup_cls, Exact):
return self.django_context.get_field_lookup_exact_type(helpers.get_typechecker_api(ctx), field)
assert lookup_cls is not None
lookup_info = helpers.lookup_class_typeinfo(helpers.get_typechecker_api(ctx), lookup_cls)
if lookup_info is None:
return AnyType(TypeOfAny.explicit)
for lookup_base in helpers.iter_bases(lookup_info):
if lookup_base.args and isinstance(lookup_base.args[0], Instance):
lookup_type: MypyType = lookup_base.args[0]
# if it's Field, consider lookup_type a __get__ of current field
if (isinstance(lookup_type, Instance)
and lookup_type.type.fullname() == fullnames.FIELD_FULLNAME):
field_info = helpers.lookup_class_typeinfo(helpers.get_typechecker_api(ctx), field.__class__)
if field_info is None:
return AnyType(TypeOfAny.explicit)
lookup_type = helpers.get_private_descriptor_type(field_info, '_pyi_private_get_type',
is_nullable=field.null)
return lookup_type
return AnyType(TypeOfAny.explicit)
class DjangoContext:
def __init__(self, django_settings_module: str) -> None:
self.fields_context = DjangoFieldsContext(self)
self.lookups_context = DjangoLookupsContext(self)
self.django_settings_module = django_settings_module
apps, settings = initialize_django(self.django_settings_module)
@@ -284,20 +124,18 @@ class DjangoContext:
if isinstance(field, ForeignObjectRel):
yield field
def get_field_lookup_exact_type(self, api: TypeChecker, field: Field) -> MypyType:
def get_field_lookup_exact_type(self, api: TypeChecker, field: Union[Field, ForeignObjectRel]) -> MypyType:
if isinstance(field, (RelatedField, ForeignObjectRel)):
related_model_cls = field.related_model
primary_key_field = self.get_primary_key_field(related_model_cls)
primary_key_type = self.fields_context.get_field_get_type(api, primary_key_field, method='init')
primary_key_type = self.get_field_get_type(api, primary_key_field, method='init')
rel_model_info = helpers.lookup_class_typeinfo(api, related_model_cls)
if rel_model_info is None:
return AnyType(TypeOfAny.explicit)
model_and_primary_key_type = UnionType.make_union([Instance(rel_model_info, []),
primary_key_type])
model_and_primary_key_type = UnionType.make_union([Instance(rel_model_info, []), primary_key_type])
return helpers.make_optional(model_and_primary_key_type)
# return helpers.make_optional(Instance(rel_model_info, []))
field_info = helpers.lookup_class_typeinfo(api, field.__class__)
if field_info is None:
@@ -319,13 +157,13 @@ class DjangoContext:
# add pk if not abstract=True
if not model_cls._meta.abstract:
primary_key_field = self.get_primary_key_field(model_cls)
field_set_type = self.fields_context.get_field_set_type(api, primary_key_field, method=method)
field_set_type = self.get_field_set_type(api, primary_key_field, method=method)
expected_types['pk'] = field_set_type
for field in model_cls._meta.get_fields():
if isinstance(field, Field):
field_name = field.attname
field_set_type = self.fields_context.get_field_set_type(api, field, method=method)
field_set_type = self.get_field_set_type(api, field, method=method)
expected_types[field_name] = field_set_type
if isinstance(field, ForeignKey):
@@ -336,7 +174,11 @@ class DjangoContext:
expected_types[field_name] = AnyType(TypeOfAny.unannotated)
continue
related_model = self.fields_context.get_related_model_cls(field)
related_model = self.get_field_related_model_cls(field)
if related_model is None:
expected_types[field_name] = AnyType(TypeOfAny.from_error)
continue
if related_model._meta.proxy_for_model is not None:
related_model = related_model._meta.proxy_for_model
@@ -345,7 +187,7 @@ class DjangoContext:
expected_types[field_name] = AnyType(TypeOfAny.unannotated)
continue
is_nullable = self.fields_context.get_field_nullability(field, method)
is_nullable = self.get_field_nullability(field, method)
foreign_key_set_type = helpers.get_private_descriptor_type(foreign_key_info,
'_pyi_private_set_type',
is_nullable=is_nullable)
@@ -365,13 +207,177 @@ class DjangoContext:
return expected_types
@cached_property
def model_base_classes(self) -> Set[str]:
def all_registered_model_classes(self) -> Set[Type[models.Model]]:
model_classes = self.apps_registry.get_models()
all_model_bases = set()
for model_cls in model_classes:
for base_cls in model_cls.mro():
if issubclass(base_cls, models.Model):
all_model_bases.add(helpers.get_class_fullname(base_cls))
all_model_bases.add(base_cls)
return all_model_bases
@cached_property
def all_registered_model_class_fullnames(self) -> Set[str]:
return {helpers.get_class_fullname(cls) for cls in self.all_registered_model_classes}
def get_attname(self, field: Field) -> str:
attname = field.attname
return attname
def get_field_nullability(self, field: Union[Field, ForeignObjectRel], method: Optional[str]) -> bool:
nullable = field.null
if not nullable and isinstance(field, CharField) and field.blank:
return True
if method == '__init__':
if ((isinstance(field, Field) and field.primary_key)
or isinstance(field, ForeignKey)):
return True
if method == 'create':
if isinstance(field, AutoField):
return True
if isinstance(field, Field) and field.has_default():
return True
return nullable
def get_field_set_type(self, api: TypeChecker, field: Union[Field, ForeignObjectRel], *, method: str) -> MypyType:
""" Get a type of __set__ for this specific Django field. """
target_field = field
if isinstance(field, ForeignKey):
target_field = field.target_field
field_info = helpers.lookup_class_typeinfo(api, target_field.__class__)
if field_info is None:
return AnyType(TypeOfAny.from_error)
field_set_type = helpers.get_private_descriptor_type(field_info, '_pyi_private_set_type',
is_nullable=self.get_field_nullability(field, method))
if isinstance(target_field, ArrayField):
argument_field_type = self.get_field_set_type(api, target_field.base_field, method=method)
field_set_type = helpers.convert_any_to_type(field_set_type, argument_field_type)
return field_set_type
def get_field_get_type(self, api: TypeChecker, field: Union[Field, ForeignObjectRel], *, method: str) -> MypyType:
""" Get a type of __get__ for this specific Django field. """
field_info = helpers.lookup_class_typeinfo(api, field.__class__)
if field_info is None:
return AnyType(TypeOfAny.unannotated)
is_nullable = self.get_field_nullability(field, method)
if isinstance(field, RelatedField):
related_model_cls = self.get_field_related_model_cls(field)
if related_model_cls is None:
return AnyType(TypeOfAny.from_error)
if method == 'values':
primary_key_field = self.get_primary_key_field(related_model_cls)
return self.get_field_get_type(api, primary_key_field, method=method)
model_info = helpers.lookup_class_typeinfo(api, related_model_cls)
if model_info is None:
return AnyType(TypeOfAny.unannotated)
return Instance(model_info, [])
else:
return helpers.get_private_descriptor_type(field_info, '_pyi_private_get_type',
is_nullable=is_nullable)
def get_field_related_model_cls(self, field: Union[RelatedField, ForeignObjectRel]) -> Optional[Type[Model]]:
if isinstance(field, RelatedField):
related_model_cls = field.remote_field.model
else:
related_model_cls = field.field.model
if isinstance(related_model_cls, str):
if related_model_cls == 'self':
# same model
related_model_cls = field.model
elif '.' not in related_model_cls:
# same file model
related_model_fullname = field.model.__module__ + '.' + related_model_cls
related_model_cls = self.get_model_class_by_fullname(related_model_fullname)
else:
related_model_cls = self.apps_registry.get_model(related_model_cls)
return related_model_cls
def _resolve_field_from_parts(self,
field_parts: Iterable[str],
model_cls: Type[Model]
) -> Union[Field, ForeignObjectRel]:
currently_observed_model = model_cls
field = None
for field_part in field_parts:
if field_part == 'pk':
field = self.get_primary_key_field(currently_observed_model)
continue
field = currently_observed_model._meta.get_field(field_part)
if isinstance(field, RelatedField):
currently_observed_model = field.related_model
model_name = currently_observed_model._meta.model_name
if (model_name is not None
and field_part == (model_name + '_id')):
field = self.get_primary_key_field(currently_observed_model)
if isinstance(field, ForeignObjectRel):
currently_observed_model = field.related_model
assert field is not None
return field
def resolve_lookup_into_field(self, model_cls: Type[Model], lookup: str) -> Union[Field, ForeignObjectRel]:
query = Query(model_cls)
lookup_parts, field_parts, is_expression = query.solve_lookup_type(lookup)
if lookup_parts:
raise LookupsAreUnsupported()
return self._resolve_field_from_parts(field_parts, model_cls)
def resolve_lookup_expected_type(self, ctx: MethodContext, model_cls: Type[Model], lookup: str) -> MypyType:
query = Query(model_cls)
try:
lookup_parts, field_parts, is_expression = query.solve_lookup_type(lookup)
if is_expression:
return AnyType(TypeOfAny.explicit)
except FieldError as exc:
ctx.api.fail(exc.args[0], ctx.context)
return AnyType(TypeOfAny.from_error)
field = self._resolve_field_from_parts(field_parts, model_cls)
lookup_cls = None
if lookup_parts:
lookup = lookup_parts[-1]
lookup_cls = field.get_lookup(lookup)
if lookup_cls is None:
# unknown lookup
return AnyType(TypeOfAny.explicit)
if lookup_cls is None or isinstance(lookup_cls, Exact):
return self.get_field_lookup_exact_type(helpers.get_typechecker_api(ctx), field)
assert lookup_cls is not None
lookup_info = helpers.lookup_class_typeinfo(helpers.get_typechecker_api(ctx), lookup_cls)
if lookup_info is None:
return AnyType(TypeOfAny.explicit)
for lookup_base in helpers.iter_bases(lookup_info):
if lookup_base.args and isinstance(lookup_base.args[0], Instance):
lookup_type: MypyType = lookup_base.args[0]
# if it's Field, consider lookup_type a __get__ of current field
if (isinstance(lookup_type, Instance)
and lookup_type.type.fullname == fullnames.FIELD_FULLNAME):
field_info = helpers.lookup_class_typeinfo(helpers.get_typechecker_api(ctx), field.__class__)
if field_info is None:
return AnyType(TypeOfAny.explicit)
lookup_type = helpers.get_private_descriptor_type(field_info, '_pyi_private_get_type',
is_nullable=field.null)
return lookup_type
return AnyType(TypeOfAny.explicit)
def resolve_f_expression_type(self, f_expression_type: Instance) -> MypyType:
return AnyType(TypeOfAny.explicit)

View File

@@ -13,7 +13,7 @@ DUMMY_SETTINGS_BASE_CLASS = 'django.conf._DjangoConfLazyObject'
QUERYSET_CLASS_FULLNAME = 'django.db.models.query.QuerySet'
BASE_MANAGER_CLASS_FULLNAME = 'django.db.models.manager.BaseManager'
MANAGER_CLASS_FULLNAME = 'django.db.models.manager.Manager'
RELATED_MANAGER_CLASS_FULLNAME = 'django.db.models.manager.RelatedManager'
RELATED_MANAGER_CLASS = 'django.db.models.manager.RelatedManager'
BASEFORM_CLASS_FULLNAME = 'django.forms.forms.BaseForm'
FORM_CLASS_FULLNAME = 'django.forms.forms.Form'
@@ -23,7 +23,7 @@ FORM_MIXIN_CLASS_FULLNAME = 'django.views.generic.edit.FormMixin'
MANAGER_CLASSES = {
MANAGER_CLASS_FULLNAME,
RELATED_MANAGER_CLASS_FULLNAME,
RELATED_MANAGER_CLASS,
BASE_MANAGER_CLASS_FULLNAME,
# QUERYSET_CLASS_FULLNAME
}
@@ -37,3 +37,5 @@ RELATED_FIELDS_CLASSES = {
MIGRATION_CLASS_FULLNAME = 'django.db.migrations.migration.Migration'
OPTIONS_CLASS_FULLNAME = 'django.db.models.options.Options'
HTTPREQUEST_CLASS_FULLNAME = 'django.http.request.HttpRequest'
F_EXPRESSION_FULLNAME = 'django.db.models.expressions.F'

View File

@@ -1,6 +1,6 @@
from collections import OrderedDict
from typing import (
TYPE_CHECKING, Any, Dict, Iterable, Iterator, List, Optional, Set, Union, cast,
TYPE_CHECKING, Any, Dict, Iterable, Iterator, List, Optional, Set, Tuple, Union,
)
from django.db.models.fields import Field
@@ -10,13 +10,15 @@ from mypy import checker
from mypy.checker import TypeChecker
from mypy.mro import calculate_mro
from mypy.nodes import (
GDEF, MDEF, Block, ClassDef, Expression, MemberExpr, MypyFile, NameExpr, StrExpr, SymbolNode, SymbolTable,
SymbolTableNode, TypeInfo, Var,
GDEF, MDEF, Argument, Block, ClassDef, Expression, FuncDef, MemberExpr, MypyFile, NameExpr, StrExpr, SymbolNode,
SymbolTable, SymbolTableNode, TypeInfo, Var,
)
from mypy.plugin import (
AttributeContext, CheckerPluginInterface, FunctionContext, MethodContext,
AttributeContext, CheckerPluginInterface, ClassDefContext, DynamicClassDefContext, FunctionContext, MethodContext,
)
from mypy.types import AnyType, Instance, NoneTyp, TupleType
from mypy.plugins.common import add_method
from mypy.semanal import SemanticAnalyzer
from mypy.types import AnyType, CallableType, Instance, NoneTyp, TupleType
from mypy.types import Type as MypyType
from mypy.types import TypedDictType, TypeOfAny, UnionType
@@ -55,7 +57,7 @@ def lookup_fully_qualified_generic(name: str, all_modules: Dict[str, MypyFile])
return sym.node
def lookup_fully_qualified_typeinfo(api: TypeChecker, fullname: str) -> Optional[TypeInfo]:
def lookup_fully_qualified_typeinfo(api: Union[TypeChecker, SemanticAnalyzer], fullname: str) -> Optional[TypeInfo]:
node = lookup_fully_qualified_generic(fullname, api.modules)
if not isinstance(node, TypeInfo):
return None
@@ -173,32 +175,30 @@ def get_nested_meta_node_for_current_class(info: TypeInfo) -> Optional[TypeInfo]
return None
def add_new_class_for_module(module: MypyFile, name: str, bases: List[Instance],
fields: 'OrderedDict[str, MypyType]') -> TypeInfo:
def add_new_class_for_module(module: MypyFile,
name: str,
bases: List[Instance],
fields: Optional[Dict[str, MypyType]] = None
) -> TypeInfo:
new_class_unique_name = checker.gen_unique_name(name, module.names)
# make new class expression
classdef = ClassDef(new_class_unique_name, Block([]))
classdef.fullname = module.fullname() + '.' + new_class_unique_name
classdef.fullname = module.fullname + '.' + new_class_unique_name
# make new TypeInfo
new_typeinfo = TypeInfo(SymbolTable(), classdef, module.fullname())
new_typeinfo = TypeInfo(SymbolTable(), classdef, module.fullname)
new_typeinfo.bases = bases
calculate_mro(new_typeinfo)
new_typeinfo.calculate_metaclass_type()
def add_field_to_new_typeinfo(var: Var, is_initialized_in_class: bool = False,
is_property: bool = False) -> None:
var.info = new_typeinfo
var.is_initialized_in_class = is_initialized_in_class
var.is_property = is_property
var._fullname = new_typeinfo.fullname() + '.' + var.name()
new_typeinfo.names[var.name()] = SymbolTableNode(MDEF, var)
# add fields
var_items = [Var(item, typ) for item, typ in fields.items()]
for var_item in var_items:
add_field_to_new_typeinfo(var_item, is_property=True)
if fields:
for field_name, field_type in fields.items():
var = Var(field_name, type=field_type)
var.info = new_typeinfo
var._fullname = new_typeinfo.fullname + '.' + field_name
new_typeinfo.names[field_name] = SymbolTableNode(MDEF, var, plugin_generated=True)
classdef.info = new_typeinfo
module.names[new_class_unique_name] = SymbolTableNode(GDEF, new_typeinfo, plugin_generated=True)
@@ -259,8 +259,7 @@ def make_typeddict(api: CheckerPluginInterface, fields: 'OrderedDict[str, MypyTy
return typed_dict_type
def resolve_string_attribute_value(attr_expr: Expression, ctx: Union[FunctionContext, MethodContext],
django_context: 'DjangoContext') -> Optional[str]:
def resolve_string_attribute_value(attr_expr: Expression, django_context: 'DjangoContext') -> Optional[str]:
if isinstance(attr_expr, StrExpr):
return attr_expr.value
@@ -270,17 +269,66 @@ def resolve_string_attribute_value(attr_expr: Expression, ctx: Union[FunctionCon
if isinstance(attr_expr.expr, NameExpr) and attr_expr.expr.fullname == 'django.conf.settings':
if hasattr(django_context.settings, member_name):
return getattr(django_context.settings, member_name)
ctx.api.fail(f'Expression of type {type(attr_expr).__name__!r} is not supported', ctx.context)
return None
def get_semanal_api(ctx: Union[ClassDefContext, DynamicClassDefContext]) -> SemanticAnalyzer:
if not isinstance(ctx.api, SemanticAnalyzer):
raise ValueError('Not a SemanticAnalyzer')
return ctx.api
def get_typechecker_api(ctx: Union[AttributeContext, MethodContext, FunctionContext]) -> TypeChecker:
if not isinstance(ctx.api, TypeChecker):
raise ValueError('Not a TypeChecker')
return cast(TypeChecker, ctx.api)
return ctx.api
def is_model_subclass_info(info: TypeInfo, django_context: 'DjangoContext') -> bool:
return (info.fullname() in django_context.model_base_classes
return (info.fullname in django_context.all_registered_model_class_fullnames
or info.has_base(fullnames.MODEL_CLASS_FULLNAME))
def check_types_compatible(ctx: Union[FunctionContext, MethodContext],
*, expected_type: MypyType, actual_type: MypyType, error_message: str) -> None:
api = get_typechecker_api(ctx)
api.check_subtype(actual_type, expected_type,
ctx.context, error_message,
'got', 'expected')
def add_new_sym_for_info(info: TypeInfo, *, name: str, sym_type: MypyType) -> None:
# type=: type of the variable itself
var = Var(name=name, type=sym_type)
# var.info: type of the object variable is bound to
var.info = info
var._fullname = info.fullname + '.' + name
var.is_initialized_in_class = True
var.is_inferred = True
info.names[name] = SymbolTableNode(MDEF, var,
plugin_generated=True)
def _prepare_new_method_arguments(node: FuncDef) -> Tuple[List[Argument], MypyType]:
arguments = []
for argument in node.arguments[1:]:
if argument.type_annotation is None:
argument.type_annotation = AnyType(TypeOfAny.unannotated)
arguments.append(argument)
if isinstance(node.type, CallableType):
return_type = node.type.ret_type
else:
return_type = AnyType(TypeOfAny.unannotated)
return arguments, return_type
def copy_method_to_another_class(ctx: ClassDefContext, self_type: Instance,
new_method_name: str, method_node: FuncDef) -> None:
arguments, return_type = _prepare_new_method_arguments(method_node)
add_method(ctx,
new_method_name,
args=arguments,
return_type=return_type,
self_type=self_type)

View File

@@ -7,15 +7,19 @@ from mypy.errors import Errors
from mypy.nodes import MypyFile, TypeInfo
from mypy.options import Options
from mypy.plugin import (
AttributeContext, ClassDefContext, FunctionContext, MethodContext, Plugin,
AttributeContext, ClassDefContext, DynamicClassDefContext, FunctionContext, MethodContext, Plugin,
)
from mypy.types import Type as MypyType
import mypy_django_plugin.transformers.orm_lookups
from mypy_django_plugin.django.context import DjangoContext
from mypy_django_plugin.lib import fullnames, helpers
from mypy_django_plugin.transformers import (
fields, forms, init_create, meta, querysets, request, settings,
)
from mypy_django_plugin.transformers.managers import (
create_new_manager_class_from_from_queryset_method,
)
from mypy_django_plugin.transformers.models import process_model_class
@@ -120,17 +124,17 @@ class NewSemanalDjangoPlugin(Plugin):
def get_additional_deps(self, file: MypyFile) -> List[Tuple[int, str, int]]:
# for settings
if file.fullname() == 'django.conf' and self.django_context.django_settings_module:
if file.fullname == 'django.conf' and self.django_context.django_settings_module:
return [self._new_dependency(self.django_context.django_settings_module)]
# for values / values_list
if file.fullname() == 'django.db.models':
if file.fullname == 'django.db.models':
return [self._new_dependency('mypy_extensions'), self._new_dependency('typing')]
# for `get_user_model()`
if self.django_context.settings:
if (file.fullname() == 'django.contrib.auth'
or file.fullname() in {'django.http', 'django.http.request'}):
if (file.fullname == 'django.contrib.auth'
or file.fullname in {'django.http', 'django.http.request'}):
auth_user_model_name = self.django_context.settings.AUTH_USER_MODEL
try:
auth_user_module = self.django_context.apps_registry.get_model(auth_user_model_name).__module__
@@ -140,7 +144,7 @@ class NewSemanalDjangoPlugin(Plugin):
return [self._new_dependency(auth_user_module)]
# ensure that all mentioned to='someapp.SomeModel' are loaded with corresponding related Fields
defined_model_classes = self.django_context.model_modules.get(file.fullname())
defined_model_classes = self.django_context.model_modules.get(file.fullname)
if not defined_model_classes:
return []
deps = set()
@@ -148,15 +152,17 @@ class NewSemanalDjangoPlugin(Plugin):
# forward relations
for field in self.django_context.get_model_fields(model_class):
if isinstance(field, RelatedField):
related_model_cls = self.django_context.fields_context.get_related_model_cls(field)
related_model_cls = self.django_context.get_field_related_model_cls(field)
if related_model_cls is None:
continue
related_model_module = related_model_cls.__module__
if related_model_module != file.fullname():
if related_model_module != file.fullname:
deps.add(self._new_dependency(related_model_module))
# reverse relations
for relation in model_class._meta.related_objects:
related_model_cls = self.django_context.fields_context.get_related_model_cls(relation)
related_model_cls = self.django_context.get_field_related_model_cls(relation)
related_model_module = related_model_cls.__module__
if related_model_module != file.fullname():
if related_model_module != file.fullname:
deps.add(self._new_dependency(related_model_module))
return list(deps)
@@ -210,12 +216,13 @@ class NewSemanalDjangoPlugin(Plugin):
if class_fullname in manager_classes and method_name == 'create':
return partial(init_create.redefine_and_typecheck_model_create, django_context=self.django_context)
if class_fullname in manager_classes and method_name in {'filter', 'get', 'exclude'}:
return partial(init_create.typecheck_queryset_filter, django_context=self.django_context)
return partial(mypy_django_plugin.transformers.orm_lookups.typecheck_queryset_filter,
django_context=self.django_context)
return None
def get_base_class_hook(self, fullname: str
) -> Optional[Callable[[ClassDefContext], None]]:
if (fullname in self.django_context.model_base_classes
if (fullname in self.django_context.all_registered_model_class_fullnames
or fullname in self._get_current_model_bases()):
return partial(transform_model_class, django_context=self.django_context)
@@ -238,6 +245,15 @@ class NewSemanalDjangoPlugin(Plugin):
return partial(request.set_auth_user_model_as_type_for_request_user, django_context=self.django_context)
return None
def get_dynamic_class_hook(self, fullname: str
) -> Optional[Callable[[DynamicClassDefContext], None]]:
if fullname.endswith('from_queryset'):
class_name, _, _ = fullname.rpartition('.')
info = self._get_typeinfo_or_none(class_name)
if info and info.has_base(fullnames.BASE_MANAGER_CLASS_FULLNAME):
return create_new_manager_class_from_from_queryset_method
return None
def plugin(version):
return NewSemanalDjangoPlugin

View File

@@ -29,7 +29,7 @@ def _get_current_field_from_assignment(ctx: FunctionContext, django_context: Dja
if field_name is None:
return None
model_cls = django_context.get_model_class_by_fullname(outer_model_info.fullname())
model_cls = django_context.get_model_class_by_fullname(outer_model_info.fullname)
if model_cls is None:
return None
@@ -37,6 +37,14 @@ def _get_current_field_from_assignment(ctx: FunctionContext, django_context: Dja
return current_field
def reparametrize_related_field_type(related_field_type: Instance, set_type, get_type) -> Instance:
args = [
helpers.convert_any_to_type(related_field_type.args[0], set_type),
helpers.convert_any_to_type(related_field_type.args[1], get_type),
]
return helpers.reparametrize_instance(related_field_type, new_args=args)
def fill_descriptor_types_for_related_field(ctx: FunctionContext, django_context: DjangoContext) -> MypyType:
current_field = _get_current_field_from_assignment(ctx, django_context)
if current_field is None:
@@ -44,7 +52,28 @@ def fill_descriptor_types_for_related_field(ctx: FunctionContext, django_context
assert isinstance(current_field, RelatedField)
related_model_cls = django_context.fields_context.get_related_model_cls(current_field)
related_model_cls = django_context.get_field_related_model_cls(current_field)
if related_model_cls is None:
return AnyType(TypeOfAny.from_error)
default_related_field_type = set_descriptor_types_for_field(ctx)
# self reference with abstract=True on the model where ForeignKey is defined
current_model_cls = current_field.model
if (current_model_cls._meta.abstract
and current_model_cls == related_model_cls):
# for all derived non-abstract classes, set variable with this name to
# __get__/__set__ of ForeignKey of derived model
for model_cls in django_context.all_registered_model_classes:
if issubclass(model_cls, current_model_cls) and not model_cls._meta.abstract:
derived_model_info = helpers.lookup_class_typeinfo(helpers.get_typechecker_api(ctx), model_cls)
if derived_model_info is not None:
fk_ref_type = Instance(derived_model_info, [])
derived_fk_type = reparametrize_related_field_type(default_related_field_type,
set_type=fk_ref_type, get_type=fk_ref_type)
helpers.add_new_sym_for_info(derived_model_info,
name=current_field.name,
sym_type=derived_fk_type)
related_model = related_model_cls
related_model_to_set = related_model_cls
@@ -67,13 +96,10 @@ def fill_descriptor_types_for_related_field(ctx: FunctionContext, django_context
else:
related_model_to_set_type = Instance(related_model_to_set_info, []) # type: ignore
default_related_field_type = set_descriptor_types_for_field(ctx)
# replace Any with referred_to_type
args = [
helpers.convert_any_to_type(default_related_field_type.args[0], related_model_to_set_type),
helpers.convert_any_to_type(default_related_field_type.args[1], related_model_type),
]
return helpers.reparametrize_instance(default_related_field_type, new_args=args)
return reparametrize_related_field_type(default_related_field_type,
set_type=related_model_to_set_type,
get_type=related_model_type)
def get_field_descriptor_types(field_info: TypeInfo, is_nullable: bool) -> Tuple[MypyType, MypyType]:

View File

@@ -6,7 +6,7 @@ from mypy.types import Instance
from mypy.types import Type as MypyType
from mypy_django_plugin.django.context import DjangoContext
from mypy_django_plugin.lib import fullnames, helpers
from mypy_django_plugin.lib import helpers
def get_actual_types(ctx: Union[MethodContext, FunctionContext],
@@ -30,12 +30,6 @@ def get_actual_types(ctx: Union[MethodContext, FunctionContext],
return actual_types
def check_types_compatible(ctx, *, expected_type, actual_type, error_message):
ctx.api.check_subtype(actual_type, expected_type,
ctx.context, error_message,
'got', 'expected')
def typecheck_model_method(ctx: Union[FunctionContext, MethodContext], django_context: DjangoContext,
model_cls: Type[Model], method: str) -> MypyType:
typechecker_api = helpers.get_typechecker_api(ctx)
@@ -48,7 +42,7 @@ def typecheck_model_method(ctx: Union[FunctionContext, MethodContext], django_co
model_cls.__name__),
ctx.context)
continue
check_types_compatible(ctx,
helpers.check_types_compatible(ctx,
expected_type=expected_types[actual_name],
actual_type=actual_type,
error_message='Incompatible type for "{}" of "{}"'.format(actual_name,
@@ -60,7 +54,7 @@ def typecheck_model_method(ctx: Union[FunctionContext, MethodContext], django_co
def redefine_and_typecheck_model_init(ctx: FunctionContext, django_context: DjangoContext) -> MypyType:
assert isinstance(ctx.default_return_type, Instance)
model_fullname = ctx.default_return_type.type.fullname()
model_fullname = ctx.default_return_type.type.fullname
model_cls = django_context.get_model_class_by_fullname(model_fullname)
if model_cls is None:
return ctx.default_return_type
@@ -73,46 +67,9 @@ def redefine_and_typecheck_model_create(ctx: MethodContext, django_context: Djan
# only work with ctx.default_return_type = model Instance
return ctx.default_return_type
model_fullname = ctx.default_return_type.type.fullname()
model_fullname = ctx.default_return_type.type.fullname
model_cls = django_context.get_model_class_by_fullname(model_fullname)
if model_cls is None:
return ctx.default_return_type
return typecheck_model_method(ctx, django_context, model_cls, 'create')
def typecheck_queryset_filter(ctx: MethodContext, django_context: DjangoContext) -> MypyType:
lookup_kwargs = ctx.arg_names[1]
provided_lookup_types = ctx.arg_types[1]
assert isinstance(ctx.type, Instance)
if not ctx.type.args or not isinstance(ctx.type.args[0], Instance):
return ctx.default_return_type
model_cls_fullname = ctx.type.args[0].type.fullname()
model_cls = django_context.get_model_class_by_fullname(model_cls_fullname)
if model_cls is None:
return ctx.default_return_type
for lookup_kwarg, provided_type in zip(lookup_kwargs, provided_lookup_types):
if lookup_kwarg is None:
continue
# Combinables are not supported yet
if (isinstance(provided_type, Instance)
and provided_type.type.has_base('django.db.models.expressions.Combinable')):
continue
lookup_type = django_context.lookups_context.resolve_lookup_expected_type(ctx, model_cls, lookup_kwarg)
# Managers as provided_type is not supported yet
if (isinstance(provided_type, Instance)
and helpers.has_any_of_bases(provided_type.type, (fullnames.MANAGER_CLASS_FULLNAME,
fullnames.QUERYSET_CLASS_FULLNAME))):
return ctx.default_return_type
check_types_compatible(ctx,
expected_type=lookup_type,
actual_type=provided_type,
error_message=f'Incompatible type for lookup {lookup_kwarg!r}:')
return ctx.default_return_type

View File

@@ -0,0 +1,71 @@
from mypy.nodes import (
GDEF, FuncDef, MemberExpr, NameExpr, StrExpr, SymbolTableNode, TypeInfo,
)
from mypy.plugin import ClassDefContext, DynamicClassDefContext
from mypy.types import AnyType, Instance, TypeOfAny
from mypy_django_plugin.lib import helpers
def create_new_manager_class_from_from_queryset_method(ctx: DynamicClassDefContext) -> None:
semanal_api = helpers.get_semanal_api(ctx)
assert isinstance(ctx.call.callee, MemberExpr)
assert isinstance(ctx.call.callee.expr, NameExpr)
base_manager_info = ctx.call.callee.expr.node
if base_manager_info is None:
if not semanal_api.final_iteration:
semanal_api.defer()
return
assert isinstance(base_manager_info, TypeInfo)
new_manager_info = semanal_api.basic_new_typeinfo(ctx.name,
basetype_or_fallback=Instance(base_manager_info,
[AnyType(TypeOfAny.unannotated)]))
new_manager_info.line = ctx.call.line
new_manager_info.defn.line = ctx.call.line
new_manager_info.metaclass_type = new_manager_info.calculate_metaclass_type()
current_module = semanal_api.cur_mod_node
current_module.names[ctx.name] = SymbolTableNode(GDEF, new_manager_info,
plugin_generated=True)
passed_queryset = ctx.call.args[0]
assert isinstance(passed_queryset, NameExpr)
derived_queryset_fullname = passed_queryset.fullname
assert derived_queryset_fullname is not None
sym = semanal_api.lookup_fully_qualified_or_none(derived_queryset_fullname)
assert sym is not None
if sym.node is None:
if not semanal_api.final_iteration:
semanal_api.defer()
else:
# inherit from Any to prevent false-positives, if queryset class cannot be resolved
new_manager_info.fallback_to_any = True
return
derived_queryset_info = sym.node
assert isinstance(derived_queryset_info, TypeInfo)
if len(ctx.call.args) > 1:
expr = ctx.call.args[1]
assert isinstance(expr, StrExpr)
custom_manager_generated_name = expr.value
else:
custom_manager_generated_name = base_manager_info.name + 'From' + derived_queryset_info.name
custom_manager_generated_fullname = '.'.join(['django.db.models.manager', custom_manager_generated_name])
if 'from_queryset_managers' not in base_manager_info.metadata:
base_manager_info.metadata['from_queryset_managers'] = {}
base_manager_info.metadata['from_queryset_managers'][custom_manager_generated_fullname] = new_manager_info.fullname
class_def_context = ClassDefContext(cls=new_manager_info.defn,
reason=ctx.call, api=semanal_api)
self_type = Instance(new_manager_info, [])
for name, sym in derived_queryset_info.names.items():
if isinstance(sym.node, FuncDef):
helpers.copy_method_to_another_class(class_def_context,
self_type,
new_method_name=name,
method_node=sym.node)

View File

@@ -28,7 +28,7 @@ def return_proper_field_type_from_get_field(ctx: MethodContext, django_context:
if not isinstance(model_type, Instance):
return ctx.default_return_type
model_cls = django_context.get_model_class_by_fullname(model_type.type.fullname())
model_cls = django_context.get_model_class_by_fullname(model_type.type.fullname)
if model_cls is None:
return ctx.default_return_type
@@ -36,7 +36,7 @@ def return_proper_field_type_from_get_field(ctx: MethodContext, django_context:
if field_name_expr is None:
return ctx.default_return_type
field_name = helpers.resolve_string_attribute_value(field_name_expr, ctx, django_context)
field_name = helpers.resolve_string_attribute_value(field_name_expr, django_context)
if field_name is None:
return ctx.default_return_type

View File

@@ -1,5 +1,4 @@
from collections import OrderedDict
from typing import Type
from typing import Dict, Optional, Type, cast
from django.db.models.base import Model
from django.db.models.fields import DateField, DateTimeField
@@ -7,12 +6,13 @@ from django.db.models.fields.related import ForeignKey
from django.db.models.fields.reverse_related import (
ManyToManyRel, ManyToOneRel, OneToOneRel,
)
from mypy.nodes import (
ARG_STAR2, MDEF, Argument, SymbolTableNode, TypeInfo, Var,
)
from mypy.nodes import ARG_STAR2, Argument, Context, FuncDef, TypeInfo, Var
from mypy.plugin import ClassDefContext
from mypy.plugins import common
from mypy.types import AnyType, Instance, TypeOfAny
from mypy.semanal import SemanticAnalyzer
from mypy.types import AnyType, Instance
from mypy.types import Type as MypyType
from mypy.types import TypeOfAny
from mypy_django_plugin.django.context import DjangoContext
from mypy_django_plugin.lib import fullnames, helpers
@@ -21,36 +21,42 @@ from mypy_django_plugin.transformers.fields import get_field_descriptor_types
class ModelClassInitializer:
api: SemanticAnalyzer
def __init__(self, ctx: ClassDefContext, django_context: DjangoContext):
self.api = ctx.api
self.api = cast(SemanticAnalyzer, ctx.api)
self.model_classdef = ctx.cls
self.django_context = django_context
self.ctx = ctx
def lookup_typeinfo(self, fullname: str) -> Optional[TypeInfo]:
return helpers.lookup_fully_qualified_typeinfo(self.api, fullname)
def lookup_typeinfo_or_incomplete_defn_error(self, fullname: str) -> TypeInfo:
sym = self.api.lookup_fully_qualified_or_none(fullname)
if sym is None or not isinstance(sym.node, TypeInfo):
info = self.lookup_typeinfo(fullname)
if info is None:
raise helpers.IncompleteDefnException(f'No {fullname!r} found')
return sym.node
return info
def lookup_class_typeinfo_or_incomplete_defn_error(self, klass: type) -> TypeInfo:
fullname = helpers.get_class_fullname(klass)
field_info = self.lookup_typeinfo_or_incomplete_defn_error(fullname)
return field_info
def create_new_var(self, name: str, typ: Instance) -> Var:
def create_new_var(self, name: str, typ: MypyType) -> Var:
# type=: type of the variable itself
var = Var(name=name, type=typ)
# var.info: type of the object variable is bound to
var.info = self.model_classdef.info
var._fullname = self.model_classdef.info.fullname() + '.' + name
var._fullname = self.model_classdef.info.fullname + '.' + name
var.is_initialized_in_class = True
var.is_inferred = True
return var
def add_new_node_to_model_class(self, name: str, typ: Instance) -> None:
var = self.create_new_var(name, typ)
self.model_classdef.info.names[name] = SymbolTableNode(MDEF, var, plugin_generated=True)
def add_new_node_to_model_class(self, name: str, typ: MypyType) -> None:
helpers.add_new_sym_for_info(self.model_classdef.info,
name=name,
sym_type=typ)
def run(self) -> None:
model_cls = self.django_context.get_model_class_by_fullname(self.model_classdef.fullname)
@@ -99,47 +105,129 @@ class AddRelatedModelsId(ModelClassInitializer):
def run_with_model_cls(self, model_cls: Type[Model]) -> None:
for field in model_cls._meta.get_fields():
if isinstance(field, ForeignKey):
related_model_cls = self.django_context.fields_context.get_related_model_cls(field)
related_model_cls = self.django_context.get_field_related_model_cls(field)
if related_model_cls is None:
error_context: Context = self.ctx.cls
field_sym = self.ctx.cls.info.get(field.name)
if field_sym is not None and field_sym.node is not None:
error_context = field_sym.node
self.api.fail(f'Cannot find model {field.related_model!r} '
f'referenced in field {field.name!r} ',
ctx=error_context)
self.add_new_node_to_model_class(field.attname,
AnyType(TypeOfAny.explicit))
continue
if related_model_cls._meta.abstract:
continue
rel_primary_key_field = self.django_context.get_primary_key_field(related_model_cls)
try:
field_info = self.lookup_class_typeinfo_or_incomplete_defn_error(rel_primary_key_field.__class__)
is_nullable = self.django_context.fields_context.get_field_nullability(field, None)
except helpers.IncompleteDefnException as exc:
if not self.api.final_iteration:
raise exc
else:
continue
is_nullable = self.django_context.get_field_nullability(field, None)
set_type, get_type = get_field_descriptor_types(field_info, is_nullable)
self.add_new_node_to_model_class(field.attname,
Instance(field_info, [set_type, get_type]))
class AddManagers(ModelClassInitializer):
def _is_manager_any(self, typ: Instance) -> bool:
return typ.type.fullname() == fullnames.MANAGER_CLASS_FULLNAME and type(typ.args[0]) == AnyType
def has_any_parametrized_manager_as_base(self, info: TypeInfo) -> bool:
for base in helpers.iter_bases(info):
if self.is_any_parametrized_manager(base):
return True
return False
def run_with_model_cls(self, model_cls: Type[Model]) -> None:
for manager_name, manager in model_cls._meta.managers_map.items():
manager_fullname = helpers.get_class_fullname(manager.__class__)
manager_info = self.lookup_typeinfo_or_incomplete_defn_error(manager_fullname)
def is_any_parametrized_manager(self, typ: Instance) -> bool:
return typ.type.fullname == fullnames.MANAGER_CLASS_FULLNAME and isinstance(typ.args[0], AnyType)
if manager_name not in self.model_classdef.info.names:
manager_type = Instance(manager_info, [Instance(self.model_classdef.info, [])])
self.add_new_node_to_model_class(manager_name, manager_type)
else:
# creates new MODELNAME_MANAGERCLASSNAME class that represents manager parametrized with current model
has_manager_any_base = any(self._is_manager_any(base) for base in manager_info.bases)
if has_manager_any_base:
custom_model_manager_name = manager.model.__name__ + '_' + manager.__class__.__name__
def get_generated_manager_mappings(self, base_manager_fullname: str) -> Dict[str, str]:
base_manager_info = self.lookup_typeinfo(base_manager_fullname)
if (base_manager_info is None
or 'from_queryset_managers' not in base_manager_info.metadata):
return {}
return base_manager_info.metadata['from_queryset_managers']
def create_new_model_parametrized_manager(self, name: str, base_manager_info: TypeInfo) -> Instance:
bases = []
for original_base in manager_info.bases:
if self._is_manager_any(original_base):
for original_base in base_manager_info.bases:
if self.is_any_parametrized_manager(original_base):
if original_base.type is None:
raise helpers.IncompleteDefnException()
original_base = helpers.reparametrize_instance(original_base,
[Instance(self.model_classdef.info, [])])
bases.append(original_base)
current_module = self.api.modules[self.model_classdef.info.module_name]
custom_manager_info = helpers.add_new_class_for_module(current_module,
custom_model_manager_name,
bases=bases,
fields=OrderedDict())
name=name, bases=bases)
# copy fields to a new manager
new_cls_def_context = ClassDefContext(cls=custom_manager_info.defn,
reason=self.ctx.reason,
api=self.api)
custom_manager_type = Instance(custom_manager_info, [Instance(self.model_classdef.info, [])])
for name, sym in base_manager_info.names.items():
# replace self type with new class, if copying method
if isinstance(sym.node, FuncDef):
helpers.copy_method_to_another_class(new_cls_def_context,
self_type=custom_manager_type,
new_method_name=name,
method_node=sym.node)
continue
new_sym = sym.copy()
if isinstance(new_sym.node, Var):
new_var = Var(name, type=sym.type)
new_var.info = custom_manager_info
new_var._fullname = custom_manager_info.fullname + '.' + name
new_sym.node = new_var
custom_manager_info.names[name] = new_sym
return custom_manager_type
def run_with_model_cls(self, model_cls: Type[Model]) -> None:
for manager_name, manager in model_cls._meta.managers_map.items():
manager_class_name = manager.__class__.__name__
manager_fullname = helpers.get_class_fullname(manager.__class__)
try:
manager_info = self.lookup_typeinfo_or_incomplete_defn_error(manager_fullname)
except helpers.IncompleteDefnException as exc:
if not self.api.final_iteration:
raise exc
else:
base_manager_fullname = helpers.get_class_fullname(manager.__class__.__bases__[0])
generated_managers = self.get_generated_manager_mappings(base_manager_fullname)
if manager_fullname not in generated_managers:
# not a generated manager, continue with the loop
continue
real_manager_fullname = generated_managers[manager_fullname]
manager_info = self.lookup_typeinfo(real_manager_fullname) # type: ignore
if manager_info is None:
continue
manager_class_name = real_manager_fullname.rsplit('.', maxsplit=1)[1]
if manager_name not in self.model_classdef.info.names:
manager_type = Instance(manager_info, [Instance(self.model_classdef.info, [])])
self.add_new_node_to_model_class(manager_name, manager_type)
else:
# creates new MODELNAME_MANAGERCLASSNAME class that represents manager parametrized with current model
if not self.has_any_parametrized_manager_as_base(manager_info):
continue
custom_model_manager_name = manager.model.__name__ + '_' + manager_class_name
try:
custom_manager_type = self.create_new_model_parametrized_manager(custom_model_manager_name,
base_manager_info=manager_info)
except helpers.IncompleteDefnException:
continue
self.add_new_node_to_model_class(manager_name, custom_manager_type)
@@ -162,15 +250,30 @@ class AddRelatedManagers(ModelClassInitializer):
# no reverse accessor
continue
related_model_cls = self.django_context.fields_context.get_related_model_cls(relation)
related_model_cls = self.django_context.get_field_related_model_cls(relation)
if related_model_cls is None:
continue
try:
related_model_info = self.lookup_class_typeinfo_or_incomplete_defn_error(related_model_cls)
except helpers.IncompleteDefnException as exc:
if not self.api.final_iteration:
raise exc
else:
continue
if isinstance(relation, OneToOneRel):
self.add_new_node_to_model_class(attname, Instance(related_model_info, []))
continue
if isinstance(relation, (ManyToOneRel, ManyToManyRel)):
manager_info = self.lookup_typeinfo_or_incomplete_defn_error(fullnames.RELATED_MANAGER_CLASS_FULLNAME)
try:
manager_info = self.lookup_typeinfo_or_incomplete_defn_error(fullnames.RELATED_MANAGER_CLASS)
except helpers.IncompleteDefnException as exc:
if not self.api.final_iteration:
raise exc
else:
continue
self.add_new_node_to_model_class(attname,
Instance(manager_info, [Instance(related_model_info, [])]))
continue

Some files were not shown because too many files have changed in this diff Show More