57 Commits

Author SHA1 Message Date
Maxim Kurnikov
1af3a12f2c bump to 1.5.0 2020-03-15 00:59:58 +03:00
Marti Raudsepp
7af89ee6a6 Update to mypy 0.770 (#341) 2020-03-13 16:45:45 +03:00
Marti Raudsepp
afa16bfb74 Make decorator functions transparent to Mypy (#306)
By declaring return type as -> Callable[[_C], _C], Mypy can infer that
the decorated function has also the same arguments and return type as
the original.

View functions are constrained to return HttpResponseBase (or any
subclass of it).

Also added typecheck test coverage to most of the cases.
2020-03-12 00:32:30 +03:00
Marti Raudsepp
f77073157b Fix CI build errors (#339)
* Updated gitpython dependency to fix error:
  ModuleNotFoundError: No module named 'gitdb.utils.compat'
* Updated Django repository git refs because old 3.0.x commit hash gave error:
  stderr: 'fatal: reference is not a tree: 6cb30414bc0f83b49afc4cae76d4af5656effe9a'
* Newer Django version also needs new ignores.
2020-03-12 00:19:51 +03:00
Kevin Marsh
fe3b95c611 Fix input types of humanize functions (#335) 2020-02-27 10:10:53 +03:00
Daniel Hillier
d0f9730c53 Add db.models.fields.Field method stubs for custom Fields (#331)
Add stubs for: pre_save, get_db_prep_value, get_db_prep_save
2020-02-17 10:08:39 +03:00
Leo Shklovskii
0fdd678d65 fix typo in README.md (#328) 2020-02-15 08:28:47 +03:00
Semyon Pupkov
2397065fa6 signing.loads allows use timedelta for max_age (#325)
* signing.loads allows use timedelta

https://github.com/django/django/blob/stable/2.2.x/django/core/signing.py#L191

* fix black
2020-02-14 14:12:58 +03:00
Semyon Pupkov
04023a9f31 Fix readme (#326) 2020-02-13 23:20:58 +03:00
Marti Raudsepp
95e6c94319 Improve test client type stubs (#322)
* Added missing `follow: bool` argument to Client request methods.
* Annotated return types as `HttpResponse` instead of `Any`.
* Made `Client` class inherit from `RequestFactory`, as it does in Django.
* Changed `json()` return type to Any, as it can also be a list.

Wrt (2), these return types were reverted from `HttpResponse` to `Any`
in commit 287c64d6fb. But I suspect that
was simply to silence mypy warnings about "incompatible with supertype
RequestFactory", not because there were any issues with the annotation.

Note that `Client.request()` monkey-patches the `HttpResponse` object
with some additional attributes. Those attributes were already annotated
before, I reordered and added additional comments to make it clear where
they come from.
2020-02-12 18:44:17 +03:00
Brian Helba
d96aee7a8b Fix the type of form.Field.widget (#321)
The `widget` class attribute of `form.Field` expects a `Widget` class, not an instance of `Widget`.

See: https://docs.djangoproject.com/en/3.0/ref/forms/fields/#widget
Also usages at: 3259983f56/django/forms/fields.py (L46)
2020-02-07 12:42:02 +03:00
Nikita Sobolev
2489bb9b04 Adds CONTRIBUTING.md (#319)
* Adds CONTRIBUTING.md

Closes #206 
Refs #274

* Update CONTRIBUTING.md
2020-02-06 15:01:56 +03:00
Nikita Sobolev
3a8f278c88 Update README.md 2020-02-06 11:09:16 +03:00
Nikita Sobolev
85b65b4578 Update README.md 2020-02-06 11:07:23 +03:00
Nikita Sobolev
150e8e862a Update README.md (#317)
* Update README.md

* Update README.md

* Update README.md
2020-02-06 11:03:38 +03:00
Vince Salvino
5b3088a17a Fix cache arg type for django.utils.cache.learn_cache_key() (#316) 2020-02-01 22:03:46 +03:00
Konstantin Alekseev
f89a0fbbaa Cleanup checks framework (#310) 2020-01-30 14:38:23 +03:00
Tatsh
438f8b5829 Add OutputWrapper write() signature (#301)
* Add OutputWrapper write() signature

* Add #type: ignore[override] to OutputWrapper write() signature
2020-01-28 01:34:13 +03:00
Joseph Kahn
836d5acd8f Allow error handlers to be strings (#307)
Based on the docs on this page: https://docs.djangoproject.com/en/3.0/ref/urls/
2020-01-28 01:33:49 +03:00
Cesar Canassa
bfae51e64c Added manager _db (#296) 2020-01-14 17:05:29 +03:00
Daniel Hillier
e3801918e3 Fix incorrect type for django.core.files.File.file (#293) (#294) 2020-01-11 14:27:46 +03:00
Marti Raudsepp
6f296b0a91 Implement @cached_property attribute inference (#292) 2020-01-10 13:57:09 +03:00
Nikita Sobolev
7ba578f6b2 Refactors validators types (#284)
* Update __init__.pyi

* It should not fail right now 🤔

* Refactor validators a bit

* Fixes CI

* Update __init__.pyi
2019-12-21 15:44:26 +03:00
Maksim Kurnikov
cb123de105 BaseManager.from_queryset(): properly resolve methods for QuerySet defined in another file (#282)
* BaseManager.from_queryset() from another file

* only anal_type per argument

* add resolve for return_type

* fix mypy errors

* remove leftover comment
2019-12-18 20:01:20 +03:00
Maksim Kurnikov
38135f2d1f Merge pull request #281 from mkurnikov/update-deps
Update dev deps, mypy to 0.760
2019-12-18 00:13:11 +03:00
Maxim Kurnikov
998b659749 bump to 1.4.0 2019-12-18 00:03:29 +03:00
Maxim Kurnikov
72f69e1c5e remove unused ignores 2019-12-18 00:02:55 +03:00
Maxim Kurnikov
d666ecd36f update dev deps, mypy to 0.760 2019-12-17 23:50:50 +03:00
Maksim Kurnikov
c1af26c027 handle return value of anal_type properly (#280) 2019-12-17 23:36:44 +03:00
Maxim Kurnikov
3c3dfcbc9f bump to 1.3.3 2019-12-17 19:20:27 +03:00
Maksim Kurnikov
1196336e3b Perform anal_type for arguments and return type when copying methods to another class (#279)
* Found the reproducible test case

* fix import resolution for method copy

* remove irrelevant parts from test

* fix mypy errors

Co-authored-by: Boger <kotvberloge@gmail.com>
2019-12-17 19:19:31 +03:00
Maksim Kurnikov
665f4d8ea1 Make related manager inherit from objects of related model (#278)
* related manager inherits from objects of related model

* fix test typechecking

* lint
2019-12-17 19:06:27 +03:00
Dima Boger
b3ed9e4827 Add inheritance QuerySet support for from_queryset (#275)
* Add testcase for queryset inheritance

* Add PoC

* Add condition for stop to looping over mro

* Change harcoded queryset class name to constant from fullnames
2019-12-16 20:16:41 +03:00
Marti Raudsepp
fb1593630a Expand stubs for django.core.management.color (#276)
Many attributes were previously missing; 'DEBUG' and 'INFO' attributes
were never supported by Django.
2019-12-16 20:16:21 +03:00
JR Heard
031d42a75d update ChoiceField's choices kwarg's annotation (#273)
per the [docs](https://docs.djangoproject.com/en/3.0/ref/forms/fields/#choicefield), `choices` is:

> Either an iterable of 2-tuples to use as choices for this field, or a callable that returns such an iterable.
2019-12-14 09:30:50 +03:00
Maxim Kurnikov
f7e1cfc6c7 bump to 1.3.2 2019-12-13 23:55:08 +03:00
Maksim Kurnikov
d0c25e3bce Allow to run from_queryset() with BaseManager, Manager (#271)
* allow to run from_queryset() with BaseManager, Manager

* fix tests
2019-12-13 20:16:33 +03:00
Maksim Kurnikov
1c31e71ffc enable 'model_forms' for tests typechecking (#270) 2019-12-13 13:30:21 +03:00
Dmitry Groshev
6b3b6be3c1 add .model field to _BaseQuerySet (#268) 2019-12-12 21:50:59 +03:00
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
89 changed files with 1475 additions and 524 deletions

1
.gitignore vendored
View File

@@ -11,3 +11,4 @@ pip-wheel-metadata/
.pytest_cache/ .pytest_cache/
/.envrc /.envrc
/.direnv /.direnv
django-sources/

View File

@@ -11,19 +11,16 @@ jobs:
- name: Typecheck Django 3.0 test suite with python 3.7 - name: Typecheck Django 3.0 test suite with python 3.7
python: 3.7 python: 3.7
script: | script: |
pip install Django==3.0.*
python ./scripts/typecheck_tests.py --django_version=3.0 python ./scripts/typecheck_tests.py --django_version=3.0
- name: Typecheck Django 3.0 test suite with python 3.6 - name: Typecheck Django 3.0 test suite with python 3.6
python: 3.6 python: 3.6
script: | script: |
pip install Django==3.0.*
python ./scripts/typecheck_tests.py --django_version=3.0 python ./scripts/typecheck_tests.py --django_version=3.0
- name: Typecheck Django 2.2 test suite with python 3.7 - name: Typecheck Django 2.2 test suite with python 3.7
python: 3.7 python: 3.7
script: | script: |
pip install Django==2.2.*
python ./scripts/typecheck_tests.py --django_version=2.2 python ./scripts/typecheck_tests.py --django_version=2.2
- name: Mypy for plugin code - name: Mypy for plugin code

111
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,111 @@
# How to contribute
## Tutorials
If you want to start working on this project,
you will need to get familiar with these projects:
- [Django docs](https://docs.djangoproject.com/en/dev/)
- [Typing in Python](https://inventwithpython.com/blog/2019/11/24/type-hints-for-busy-python-programmers/)
- [How to write custom mypy plugins](https://mypy.readthedocs.io/en/stable/extending_mypy.html)
- [Typechecking Django and DRF](https://sobolevn.me/2019/08/typechecking-django-and-drf) guide
- [Testing mypy stubs, plugins, and types](https://sobolevn.me/2019/08/testing-mypy-types) guide
It is also recommended to take a look at these resources:
- [Awesome Python Typing](https://github.com/typeddjango/awesome-python-typing)
## Dev documentation
TODO
## Dependencies
We use `pip` to manage the dependencies.
To install them you would need to activate your `virtualenv` and run `install` command:
```bash
pip install -r ./dev-requirements.txt
```
## Tests and linters
We use `mypy`, `pytest`, `flake8`, and `black` for quality control.
Here's [how we run our CI](https://github.com/typeddjango/django-stubs/blob/master/.travis.yml).
### Typechecking
To run typechecking use:
```bash
mypy ./mypy_django_plugin
```
### Testing
There are unit tests and type-related tests.
To run unit tests:
```bash
pytest
```
Type-related tests ensure that different Django versions do work correctly.
To run type-related tests:
```bash
python ./scripts/typecheck_tests.py --django_version=2.2
python ./scripts/typecheck_tests.py --django_version=3.0
```
Currently we only support two Django versions.
### Linting
To run auto-formatting:
```bash
isort -rc .
black django-stubs/
```
To run linting:
```bash
flake8
flake8 --config flake8-pyi.ini
```
## Submitting your code
We use [trunk based](https://trunkbaseddevelopment.com/)
development (we also sometimes call it `wemake-git-flow`).
What the point of this method?
1. We use protected `master` branch,
so the only way to push your code is via pull request
2. We use issue branches: to implement a new feature or to fix a bug
create a new branch named `issue-$TASKNUMBER`
3. Then create a pull request to `master` branch
4. We use `git tag`s to make releases, so we can track what has changed
since the latest release
So, this way we achieve an easy and scalable development process
which frees us from merging hell and long-living branches.
In this method, the latest version of the app is always in the `master` branch.
## Other help
You can contribute by spreading a word about this library.
It would also be a huge contribution to write
a short article on how you are using this project.
You can also share your best practices with us.

103
README.md
View File

@@ -1,14 +1,12 @@
<img src="http://mypy-lang.org/static/mypy_light.svg" alt="mypy logo" width="300px"/> <img src="http://mypy-lang.org/static/mypy_light.svg" alt="mypy logo" width="300px"/>
# pep484 stubs for Django framework # Typesafe Django Framework
[![Build Status](https://travis-ci.com/typeddjango/django-stubs.svg?branch=master)](https://travis-ci.com/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/) [![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) [![Gitter](https://badges.gitter.im/mypy-django/Lobby.svg)](https://gitter.im/mypy-django/Lobby)
This package contains type stubs and mypy plugin to provide more precise static types and type inference for Django framework. Django uses some Python "magic" that makes having precise types for some code patterns problematic. This is why we need to accompany the stubs with mypy plugins. The final goal is to be able to get precise types for most common patterns. This package contains [type stubs](https://www.python.org/dev/peps/pep-0561/) and a custom mypy plugin to provide more precise static types and type inference for Django framework. Django uses some Python "magic" that makes having precise types for some code patterns problematic. This is why we need this project. The final goal is to be able to get precise types for most common patterns.
Could be run on earlier versions of Django, but expect some missing imports warnings.
## Installation ## Installation
@@ -17,8 +15,35 @@ Could be run on earlier versions of Django, but expect some missing imports warn
pip install django-stubs pip install django-stubs
``` ```
See [Configuration](#configuration) section to get started.
## Mypy compatibility
## Configuration
To make `mypy` happy, you will need to add:
```ini
[mypy]
plugins =
mypy_django_plugin.main
[mypy.plugins.django-stubs]
django_settings_module = "myproject.settings"
```
in your `mypy.ini` or `setup.cfg` [file](https://mypy.readthedocs.io/en/latest/config_file.html).
Two things happeining here:
1. We need to explicitly list our plugin to be loaded by `mypy`
2. Our plugin also requires `django` settings module (what you put into `DJANGO_SETTINGS_MODULE` variable) to be specified
This fully working [typed boilerplate](https://github.com/wemake-services/wemake-django-template) can serve you as an example.
## Version compatibility
We rely on different `django` and `mypy` versions:
| django-stubs | mypy version | django version | python version | django-stubs | mypy version | django version | python version
| ------------ | ---- | ---- | ---- | | ------------ | ---- | ---- | ---- |
@@ -28,58 +53,66 @@ pip install django-stubs
| 0.12.x | old semantic analyzer (<0.711), dmypy support | 2.1.x | ^3.6 | 0.12.x | old semantic analyzer (<0.711), dmypy support | 2.1.x | ^3.6
## Configuration ## FAQ
To make mypy aware of the plugin, you need to add ### Is this an official Django project?
```ini No, it is not. We are indendepent from Django at the moment.
[mypy] There's a [proposal](https://github.com/django/deps/pull/65) to merge our project into the Django itself.
plugins = You show your support by linking the PR.
mypy_django_plugin.main
```
in your `mypy.ini` or `setup.cfg` [file](https://mypy.readthedocs.io/en/latest/config_file.html). ### Is it safe to use this in production?
Plugin also requires Django settings module (what you put into `DJANGO_SETTINGS_MODULE` variable) to be specified. Yes, it is! This project does not affect your runtime at all.
It only affects `mypy` type checking process.
```ini But, it does not make any sense to use this project without `mypy`.
[mypy]
strict_optional = True
# This one is new: ### mypy crashes when I run it with this plugin installed
[mypy.plugins.django-stubs]
django_settings_module = mysettings
```
Where `mysettings` is a value of `DJANGO_SETTINGS_MODULE` (with or without quotes) Current implementation uses Django runtime to extract models information, so it will crash, if your installed apps or `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()`.
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. In other words, if your `manage.py runserver` crashes, mypy will crash too.
You can also run `mypy` with [`--tb`](https://mypy.readthedocs.io/en/stable/command_line.html#cmdoption-mypy-show-traceback)
option to get extra information about the error.
This fully working [typed boilerplate](https://github.com/wemake-services/wemake-django-template) can serve you as an example. ### I cannot use QuerySet or Manager with type annotations
You can get a `TypeError: 'type' object is not subscriptable`
when you will try to use `QuerySet[MyModel]` or `Manager[MyModel]`.
## Notes This happens because Django classes do not support [`__class_getitem__`](https://www.python.org/dev/peps/pep-0560/#class-getitem) magic method.
Type implementation monkey-patches Django to add `__class_getitem__` to the `Manager` class. You can use strings instead: `'QuerySet[MyModel]'` and `'Manager[MyModel]'`, this way it will work as a type for `mypy` and as a regular `str` in runtime.
If you would use Python3.7 and do that too in your code, you can make things like
Currently we [are working](https://github.com/django/django/pull/12405) on providing `__class_getitem__` to the classes where we need them.
### How can I use HttpRequest with custom user model?
You can subclass standard request like so:
```python ```python
class MyUserManager(models.Manager['MyUser']): from django.http import HttpRequest
pass from my_user_app.models import MyUser
class MyUser(models.Model): class MyRequest(HttpRequest):
objects = MyUserManager() user: MyUser
``` ```
work, which should make a error messages a bit better. And then use `MyRequest` instead of standard `HttpRequest` inside your project.
## Related projects
- [`awesome-python-typing`](https://github.com/typeddjango/awesome-python-typing) - Awesome list of all typing-related things in Python.
- [`djangorestframework-stubs`](https://github.com/typeddjango/djangorestframework-stubs) - Stubs for Django REST Framework.
- [`pytest-mypy-plugins`](https://github.com/typeddjango/pytest-mypy-plugins) - `pytest` plugin that we use for testing `mypy` stubs and plugins.
- [`wemake-django-template`](https://github.com/wemake-services/wemake-django-template) - Create new typed Django projects in seconds.
Otherwise, custom type will be created in mypy, named `MyUser__MyUserManager`, which will rewrite base manager as `models.Manager[User]` to make methods like `get_queryset()` and others return properly typed `QuerySet`.
## To get help ## To get help
We have Gitter here: <https://gitter.im/mypy-django/Lobby> We have Gitter here: <https://gitter.im/mypy-django/Lobby>
If you think you have more generic typing issue, please refer to https://github.com/python/mypy and their Gitter. If you think you have more generic typing issue, please refer to <https://github.com/python/mypy> and their Gitter.

View File

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

View File

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

View File

@@ -5,10 +5,10 @@ from django.http.response import HttpResponse, HttpResponseBase
from django.urls import URLResolver, URLPattern from django.urls import URLResolver, URLPattern
handler400: Callable[..., HttpResponse] = ... handler400: Union[str, Callable[..., HttpResponse]] = ...
handler403: Callable[..., HttpResponse] = ... handler403: Union[str, Callable[..., HttpResponse]] = ...
handler404: Callable[..., HttpResponse] = ... handler404: Union[str, Callable[..., HttpResponse]] = ...
handler500: Callable[..., HttpResponse] = ... handler500: Union[str, Callable[..., HttpResponse]] = ...
IncludedURLConf = Tuple[List[URLResolver], Optional[str], Optional[str]] IncludedURLConf = Tuple[List[URLResolver], Optional[str], Optional[str]]

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.contrib.admin.options import BaseModelAdmin
from django.core.checks.messages import Error from django.core.checks.messages import Error
from django.apps.config import AppConfig
_CheckError = Union[str, Error] _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]: ... def check_dependencies(**kwargs: Any) -> List[_CheckError]: ...
class BaseModelAdminChecks: class BaseModelAdminChecks:

View File

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

View File

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

View File

@@ -1,7 +1,7 @@
from typing import Any, Optional, Set, Union from typing import Any, Optional, Set, Union
from django.contrib.auth.base_user import AbstractBaseUser 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 from django.db.models.base import Model
@@ -9,17 +9,26 @@ _AnyUser = Union[Model, AnonymousUser]
UserModel: Any UserModel: Any
class ModelBackend: class BaseBackend:
def authenticate( def authenticate(
self, request: Any, username: Optional[str] = ..., password: Optional[str] = ..., **kwargs: Any self, request: Any, username: Optional[str] = ..., password: Optional[str] = ..., **kwargs: Any
) -> Optional[AbstractBaseUser]: ... ) -> Optional[AbstractBaseUser]: ...
def user_can_authenticate(self, user: Optional[_AnyUser]) -> bool: ... def get_user(self, user_id: int) -> Optional[AbstractBaseUser]: ...
def get_user_permissions(self, user_obj: _AnyUser, obj: Optional[Model] = ...) -> Set[str]: ... 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_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 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: ... 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 has_module_perms(self, user_obj: _AnyUser, app_label: str) -> bool: ...
def get_user(self, user_id: int) -> AbstractBaseUser: ... 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): ... class AllowAllUsersModelBackend(ModelBackend): ...

View File

@@ -13,11 +13,10 @@ class BaseUserManager(models.Manager[_T]):
def get_by_natural_key(self, username: Optional[str]) -> _T: ... def get_by_natural_key(self, username: Optional[str]) -> _T: ...
class AbstractBaseUser(models.Model): class AbstractBaseUser(models.Model):
REQUIRED_FIELDS: List[str] = ...
password = models.CharField(max_length=128) password = models.CharField(max_length=128)
last_login = models.DateTimeField(blank=True, null=True) last_login = models.DateTimeField(blank=True, null=True)
REQUIRED_FIELDS: List[str] = ...
class Meta: ...
def get_username(self) -> str: ... def get_username(self) -> str: ...
def natural_key(self) -> Tuple[str]: ... def natural_key(self) -> Tuple[str]: ...
@property @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 from django.core.checks.messages import CheckMessage
def check_user_model(app_configs: None = ..., **kwargs: Any) -> List[CheckMessage]: ... from django.apps.config import AppConfig
def check_models_permissions(app_configs: None = ..., **kwargs: Any) -> List[Any]: ...
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,13 +1,21 @@
from typing import Callable, List, Optional, Set, Union from typing import Callable, List, Optional, Set, Union, TypeVar, overload
from django.contrib.auth import REDIRECT_FIELD_NAME as REDIRECT_FIELD_NAME # noqa: F401 from django.contrib.auth import REDIRECT_FIELD_NAME as REDIRECT_FIELD_NAME # noqa: F401
from django.http.response import HttpResponseBase
from django.contrib.auth.models import AbstractUser
_VIEW = TypeVar("_VIEW", bound=Callable[..., HttpResponseBase])
def user_passes_test( def user_passes_test(
test_func: Callable, login_url: Optional[str] = ..., redirect_field_name: str = ... test_func: Callable[[AbstractUser], bool], login_url: Optional[str] = ..., redirect_field_name: str = ...
) -> Callable: ... ) -> Callable[[_VIEW], _VIEW]: ...
def login_required(
function: Optional[Callable] = ..., redirect_field_name: str = ..., login_url: Optional[str] = ... # There are two ways of calling @login_required: @with(arguments) and @bare
) -> Callable: ... @overload
def login_required(redirect_field_name: str = ..., login_url: Optional[str] = ...) -> Callable[[_VIEW], _VIEW]: ...
@overload
def login_required(function: _VIEW, redirect_field_name: str = ..., login_url: Optional[str] = ...) -> _VIEW: ...
def permission_required( def permission_required(
perm: Union[List[str], Set[str], str], login_url: None = ..., raise_exception: bool = ... perm: Union[List[str], Set[str], str], login_url: None = ..., raise_exception: bool = ...
) -> Callable: ... ) -> Callable[[_VIEW], _VIEW]: ...

View File

@@ -1,5 +1,6 @@
from typing import Any, Collection, Optional, Set, Tuple, Type, TypeVar, Union 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.base_user import AbstractBaseUser as AbstractBaseUser, BaseUserManager as BaseUserManager
from django.contrib.auth.validators import UnicodeUsernameValidator from django.contrib.auth.validators import UnicodeUsernameValidator
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
@@ -43,11 +44,20 @@ class UserManager(BaseUserManager[_T]):
def create_superuser( def create_superuser(
self, username: str, email: Optional[str], password: Optional[str], **extra_fields: Any self, username: str, email: Optional[str], password: Optional[str], **extra_fields: Any
) -> _T: ... ) -> _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): class PermissionsMixin(models.Model):
is_superuser = models.BooleanField() is_superuser = models.BooleanField()
groups = models.ManyToManyField(Group) groups = models.ManyToManyField(Group)
user_permissions = models.ManyToManyField(Permission) 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_group_permissions(self, obj: Optional[_AnyUser] = ...) -> Set[str]: ...
def get_all_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_perm(self, perm: str, obj: Optional[_AnyUser] = ...) -> bool: ...
@@ -88,6 +98,7 @@ class AnonymousUser:
def groups(self) -> EmptyManager: ... def groups(self) -> EmptyManager: ...
@property @property
def user_permissions(self) -> EmptyManager: ... def user_permissions(self) -> EmptyManager: ...
def get_user_permissions(self, obj: Optional[_AnyUser] = ...) -> Set[str]: ...
def get_group_permissions(self, obj: Optional[_AnyUser] = ...) -> Set[Any]: ... def get_group_permissions(self, obj: Optional[_AnyUser] = ...) -> Set[Any]: ...
def get_all_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_perm(self, perm: str, obj: Optional[_AnyUser] = ...) -> bool: ...

View File

@@ -55,6 +55,7 @@ class PasswordResetDoneView(PasswordContextMixin, TemplateView):
class PasswordResetConfirmView(PasswordContextMixin, FormView): class PasswordResetConfirmView(PasswordContextMixin, FormView):
post_reset_login: bool = ... post_reset_login: bool = ...
post_reset_login_backend: Any = ... post_reset_login_backend: Any = ...
reset_url_token: str = ...
title: Any = ... title: Any = ...
token_generator: Any = ... token_generator: Any = ...
validlink: bool = ... 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]: ... from django.apps.config import AppConfig
def check_model_name_lengths(app_configs: None = ..., **kwargs: Any) -> List[Any]: ...
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

@@ -1,15 +1,14 @@
from datetime import date, datetime as datetime from datetime import date, datetime as datetime
from decimal import Decimal from typing import Any, Optional, SupportsInt, Union
from typing import Any, Optional, Union
register: Any register: Any
def ordinal(value: Optional[str]) -> Optional[str]: ... def ordinal(value: Optional[Union[str, SupportsInt]]) -> Optional[str]: ...
def intcomma(value: Optional[Union[Decimal, float, str]], use_l10n: bool = ...) -> str: ... def intcomma(value: Optional[Union[str, SupportsInt]], use_l10n: bool = ...) -> str: ...
intword_converters: Any intword_converters: Any
def intword(value: Optional[str]) -> Optional[Union[int, str]]: ... def intword(value: Optional[Union[str, SupportsInt]]) -> Optional[Union[int, str]]: ...
def apnumber(value: Optional[str]) -> Optional[Union[int, str]]: ... def apnumber(value: Optional[Union[str, SupportsInt]]) -> Optional[Union[int, str]]: ...
def naturalday(value: Optional[Union[date, str]], arg: None = ...) -> Optional[str]: ... def naturalday(value: Optional[Union[date, str]], arg: None = ...) -> Optional[str]: ...
def naturaltime(value: datetime) -> str: ... def naturaltime(value: datetime) -> str: ...

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, FloatRangeField as FloatRangeField,
DateRangeField as DateRangeField, DateRangeField as DateRangeField,
DateTimeRangeField as DateTimeRangeField, DateTimeRangeField as DateTimeRangeField,
RangeOperators as RangeOperators,
RangeBoundary as RangeBoundary,
) )
from .hstore import HStoreField as HStoreField from .hstore import HStoreField as HStoreField
from .citext import ( from .citext import (

View File

@@ -29,3 +29,20 @@ class DateTimeRangeField(RangeField):
class DateRangeField(RangeField): class DateRangeField(RangeField):
def __get__(self, instance, owner) -> DateRange: ... 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 typing import Any
from django.apps.config import AppConfig
from django.apps.registry import Apps from django.apps.registry import Apps
from django.contrib.sites.apps import SitesConfig
def create_default_site( def create_default_site(
app_config: SitesConfig, app_config: AppConfig,
verbosity: int = ..., verbosity: int = ...,
interactive: bool = ..., interactive: bool = ...,
using: str = ..., using: str = ...,

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 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

@@ -1,5 +1,17 @@
from .messages import Warning as Warning, Info as Info, Debug as Debug, Error as Error, Critical as Critical from .messages import (
CheckMessage as CheckMessage,
Debug as Debug,
Info as Info,
Warning as Warning,
Error as Error,
Critical as Critical,
DEBUG as DEBUG,
INFO as INFO,
WARNING as WARNING,
ERROR as ERROR,
CRITICAL as CRITICAL,
)
from .registry import run_checks as run_checks, Tags as Tags, register as register from .registry import register as register, run_checks as run_checks, tag_exists as tag_exists, Tags as Tags
from . import model_checks as model_checks from . import model_checks as model_checks

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.core.checks.messages import Error
from django.apps.config import AppConfig
E001: Any 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

@@ -7,11 +7,11 @@ ERROR: int
CRITICAL: int CRITICAL: int
class CheckMessage: class CheckMessage:
level: Any = ... level: int = ...
msg: Any = ... msg: str = ...
hint: Any = ... hint: Optional[str] = ...
obj: Any = ... obj: Any = ...
id: Any = ... id: Optional[str] = ...
def __init__( def __init__(
self, level: int, msg: str, hint: Optional[str] = ..., obj: Any = ..., id: Optional[str] = ... self, level: int, msg: str, hint: Optional[str] = ..., obj: Any = ..., id: Optional[str] = ...
) -> None: ... ) -> None: ...
@@ -25,19 +25,9 @@ class Info(CheckMessage):
def __init__(self, *args: Any, **kwargs: Any) -> None: ... def __init__(self, *args: Any, **kwargs: Any) -> None: ...
class Warning(CheckMessage): class Warning(CheckMessage):
hint: str
id: str
level: int
msg: str
obj: Any
def __init__(self, *args: Any, **kwargs: Any) -> None: ... def __init__(self, *args: Any, **kwargs: Any) -> None: ...
class Error(CheckMessage): class Error(CheckMessage):
hint: None
id: str
level: int
msg: str
obj: Any
def __init__(self, *args: Any, **kwargs: Any) -> None: ... def __init__(self, *args: Any, **kwargs: Any) -> None: ...
class Critical(CheckMessage): class Critical(CheckMessage):

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 from django.core.checks.messages import Warning
def check_all_models(app_configs: None = ..., **kwargs: Any) -> List[Warning]: ... from django.apps.config import AppConfig
def check_lazy_references(app_configs: None = ..., **kwargs: Any) -> List[Any]: ...
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

@@ -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.core.checks.messages import Warning
from django.apps.config import AppConfig
SECRET_KEY_MIN_LENGTH: int SECRET_KEY_MIN_LENGTH: int
SECRET_KEY_MIN_UNIQUE_CHARACTERS: int SECRET_KEY_MIN_UNIQUE_CHARACTERS: int
W001: Any W001: Any
@@ -17,15 +19,15 @@ W019: Any
W020: Any W020: Any
W021: Any W021: Any
def check_security_middleware(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: None, **kwargs: Any) -> List[Warning]: ... def check_xframe_options_middleware(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Warning]: ...
def check_sts(app_configs: None, **kwargs: Any) -> List[Warning]: ... def check_sts(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Warning]: ...
def check_sts_include_subdomains(app_configs: None, **kwargs: Any) -> List[Warning]: ... def check_sts_include_subdomains(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Warning]: ...
def check_sts_preload(app_configs: None, **kwargs: Any) -> List[Warning]: ... def check_sts_preload(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Warning]: ...
def check_content_type_nosniff(app_configs: None, **kwargs: Any) -> List[Warning]: ... def check_content_type_nosniff(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Warning]: ...
def check_xss_filter(app_configs: None, **kwargs: Any) -> List[Warning]: ... def check_xss_filter(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Warning]: ...
def check_ssl_redirect(app_configs: None, **kwargs: Any) -> List[Warning]: ... def check_ssl_redirect(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Warning]: ...
def check_secret_key(app_configs: None, **kwargs: Any) -> List[Warning]: ... def check_secret_key(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Warning]: ...
def check_debug(app_configs: None, **kwargs: Any) -> List[Warning]: ... def check_debug(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Warning]: ...
def check_xframe_deny(app_configs: None, **kwargs: Any) -> List[Warning]: ... def check_xframe_deny(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Warning]: ...
def check_allowed_hosts(app_configs: None, **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.core.checks.messages import Warning
from django.apps.config import AppConfig
W003: Any W003: Any
W016: Any W016: Any
def check_csrf_middleware(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: None, **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.core.checks.messages import Warning
from django.apps.config import AppConfig
def add_session_cookie_message(message: Any): ... def add_session_cookie_message(message: Any): ...
W010: Any W010: Any
@@ -14,5 +16,5 @@ W013: Any
W014: Any W014: Any
W015: Any W015: Any
def check_session_cookie_secure(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: None, **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.core.checks.messages import Error
from django.apps.config import AppConfig
E001: Any E001: Any
E002: Any E002: Any
def check_setting_app_dirs_loaders(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: None, **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.core.checks.messages import CheckMessage, Error, Warning
from django.urls.resolvers import URLPattern, URLResolver 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_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 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: ... def E006(name: str) -> Error: ...

View File

@@ -8,7 +8,7 @@ _T = TypeVar("_T", bound="File")
class File(FileProxyMixin, IO[Any]): class File(FileProxyMixin, IO[Any]):
DEFAULT_CHUNK_SIZE: Any = ... DEFAULT_CHUNK_SIZE: Any = ...
file: StringIO = ... file: IO[Any] = ...
name: str = ... name: str = ...
mode: str = ... mode: str = ...
def __init__(self, file: Any, name: Optional[str] = ...) -> None: ... def __init__(self, file: Any, name: Optional[str] = ...) -> None: ...

View File

@@ -23,13 +23,16 @@ class OutputWrapper(TextIOBase):
@property @property
def style_func(self): ... def style_func(self): ...
@style_func.setter @style_func.setter
def style_func(self, style_func: Any): ... def style_func(self, style_func: Callable[[str], str]): ...
ending: str = ... ending: str = ...
def __init__( def __init__(
self, out: Union[StringIO, TextIOWrapper], style_func: Optional[Callable] = ..., ending: str = ... self, out: Union[StringIO, TextIOWrapper], style_func: Optional[Callable[[str], str]] = ..., ending: str = ...
) -> None: ... ) -> None: ...
def __getattr__(self, name: str) -> Callable: ... def __getattr__(self, name: str) -> Callable: ...
def isatty(self) -> bool: ... def isatty(self) -> bool: ...
def write( # type: ignore[override]
self, msg: str, style_func: Optional[Callable[[str], str]] = ..., ending: Optional[str] = ...
) -> None: ...
class BaseCommand: class BaseCommand:
help: str = ... help: str = ...

View File

@@ -1,11 +1,24 @@
def supports_color() -> bool: ... def supports_color() -> bool: ...
class Style: class Style:
def DEBUG(self, text: str) -> str: ... def ERROR(self, text: str) -> str: ...
def INFO(self, text: str) -> str: ...
def SUCCESS(self, text: str) -> str: ... def SUCCESS(self, text: str) -> str: ...
def WARNING(self, text: str) -> str: ... def WARNING(self, text: str) -> str: ...
def ERROR(self, text: str) -> str: ... def NOTICE(self, text: str) -> str: ...
def SQL_FIELD(self, text: str) -> str: ...
def SQL_COLTYPE(self, text: str) -> str: ...
def SQL_KEYWORD(self, text: str) -> str: ...
def SQL_TABLE(self, text: str) -> str: ...
def HTTP_INFO(self, text: str) -> str: ...
def HTTP_SUCCESS(self, text: str) -> str: ...
def HTTP_REDIRECT(self, text: str) -> str: ...
def HTTP_NOT_MODIFIED(self, text: str) -> str: ...
def HTTP_BAD_REQUEST(self, text: str) -> str: ...
def HTTP_NOT_FOUND(self, text: str) -> str: ...
def HTTP_SERVER_ERROR(self, text: str) -> str: ...
def MIGRATE_HEADING(self, text: str) -> str: ...
def MIGRATE_LABEL(self, text: str) -> str: ...
def ERROR_OUTPUT(self, text: str) -> str: ...
def make_style(config_string: str = ...) -> Style: ... def make_style(config_string: str = ...) -> Style: ...
def no_style() -> Style: ... def no_style() -> Style: ...

View File

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

View File

@@ -1,24 +1,10 @@
import json import json
from datetime import datetime from typing import Any, Dict
from decimal import Decimal
from io import TextIOWrapper
from typing import Any, Union, Dict
from uuid import UUID
from django.core.serializers.python import Serializer as PythonSerializer from django.core.serializers.python import Serializer as PythonSerializer
from django.db.models.base import Model
class Serializer(PythonSerializer): class Serializer(PythonSerializer):
json_kwargs: Dict[str, Any] 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: ... def Deserializer(stream_or_string: Any, **options: Any) -> None: ...
@@ -29,4 +15,3 @@ class DjangoJSONEncoder(json.JSONEncoder):
indent: int indent: int
skipkeys: bool skipkeys: bool
sort_keys: bool sort_keys: bool
def default(self, o: Union[datetime, Decimal, UUID]) -> str: ...

View File

@@ -1,31 +1,15 @@
from collections import OrderedDict from collections import OrderedDict
from io import TextIOWrapper from typing import Any, Dict, Iterator, List, Optional
from typing import Any, Dict, Iterator, List
from django.core.serializers.base import DeserializedObject from django.core.serializers.base import DeserializedObject
from django.db.models.base import Model from django.db.models.base import Model
from django.db.models.fields.related import ForeignKey, ManyToManyField
from django.core.serializers import base from django.core.serializers import base
from django.db.models.fields import Field
class Serializer(base.Serializer): 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] = ... 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 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( 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]: ... ) -> Iterator[DeserializedObject]: ...

View File

@@ -21,7 +21,11 @@ def dumps(
obj: Any, key: None = ..., salt: str = ..., serializer: Type[Serializer] = ..., compress: bool = ... obj: Any, key: None = ..., salt: str = ..., serializer: Type[Serializer] = ..., compress: bool = ...
) -> str: ... ) -> str: ...
def loads( def loads(
s: str, key: None = ..., salt: str = ..., serializer: Type[Serializer] = ..., max_age: Optional[int] = ... s: str,
key: None = ...,
salt: str = ...,
serializer: Type[Serializer] = ...,
max_age: Optional[Union[int, timedelta]] = ...,
) -> Any: ... ) -> Any: ...
class Signer: class Signer:

View File

@@ -1,28 +1,26 @@
from datetime import datetime
from decimal import Decimal from decimal import Decimal
from re import RegexFlag from re import RegexFlag
from typing import Any, Dict, List, Optional, Union, Pattern, Collection from typing import Any, Callable, Collection, Dict, List, Optional, Pattern, Tuple, Union
from uuid import UUID
from django.core.files.base import File from django.core.files.base import File
from django.core.exceptions import ValidationError as ValidationError # noqa: F401
EMPTY_VALUES: Any EMPTY_VALUES: Any
_Regex = Union[str, Pattern[str]] _Regex = Union[str, Pattern[str]]
_ErrorMessage = Union[str, Any]
def _lazy_re_compile(regex: _Regex, flags: int = ...): ... def _lazy_re_compile(regex: _Regex, flags: int = ...): ...
class RegexValidator: class RegexValidator:
regex: _Regex = ... regex: _Regex = ...
message: Any = ... message: str = ...
code: str = ... code: str = ...
inverse_match: bool = ... inverse_match: bool = ...
flags: int = ... flags: int = ...
def __init__( def __init__(
self, self,
regex: Optional[_Regex] = ..., regex: Optional[_Regex] = ...,
message: Optional[str] = ..., message: Optional[_ErrorMessage] = ...,
code: Optional[str] = ..., code: Optional[str] = ...,
inverse_match: Optional[bool] = ..., inverse_match: Optional[bool] = ...,
flags: Optional[RegexFlag] = ..., flags: Optional[RegexFlag] = ...,
@@ -33,95 +31,82 @@ class URLValidator(RegexValidator):
ul: str = ... ul: str = ...
ipv4_re: str = ... ipv4_re: str = ...
ipv6_re: str = ... ipv6_re: str = ...
hostname_re: Any = ... hostname_re: str = ...
domain_re: Any = ... domain_re: str = ...
tld_re: Any = ... tld_re: str = ...
host_re: Any = ... host_re: str = ...
schemes: Any = ... schemes: List[str] = ...
def __init__(self, schemes: Optional[Collection[str]] = ..., **kwargs: Any) -> None: ... def __init__(self, schemes: Optional[Collection[str]] = ..., **kwargs: Any) -> None: ...
integer_validator: Any integer_validator: RegexValidator = ...
def validate_integer(value: Optional[Union[float, str]]) -> None: ... def validate_integer(value: Optional[Union[float, str]]) -> None: ...
class EmailValidator: class EmailValidator:
message: Any = ... message: str = ...
code: str = ... code: str = ...
user_regex: Any = ... user_regex: Pattern = ...
domain_regex: Any = ... domain_regex: Pattern = ...
literal_regex: Any = ... literal_regex: Pattern = ...
domain_whitelist: Any = ... domain_whitelist: List[str] = ...
def __init__( 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: ... ) -> None: ...
def __call__(self, value: Optional[str]) -> None: ... def __call__(self, value: Optional[str]) -> None: ...
def validate_domain_part(self, domain_part: str) -> bool: ... def validate_domain_part(self, domain_part: str) -> bool: ...
validate_email: Any validate_email: EmailValidator = ...
slug_re: Any slug_re: Pattern = ...
validate_slug: Any validate_slug: RegexValidator = ...
slug_unicode_re: Any slug_unicode_re: Pattern = ...
validate_unicode_slug: Any validate_unicode_slug: RegexValidator = ...
def validate_ipv4_address(value: str) -> None: ... def validate_ipv4_address(value: str) -> None: ...
def validate_ipv6_address(value: str) -> None: ... def validate_ipv6_address(value: str) -> None: ...
def validate_ipv46_address(value: str) -> None: ... def validate_ipv46_address(value: str) -> None: ...
ip_address_validator_map: Any _IPValidator = Tuple[Callable[[Any], None], str]
ip_address_validator_map: Dict[str, _IPValidator]
def ip_address_validators(protocol: str, unpack_ipv4: bool) -> Any: ... def ip_address_validators(protocol: str, unpack_ipv4: bool) -> _IPValidator: ...
def int_list_validator( def int_list_validator(
sep: str = ..., message: None = ..., code: str = ..., allow_negative: bool = ... sep: str = ..., message: Optional[_ErrorMessage] = ..., code: str = ..., allow_negative: bool = ...
) -> RegexValidator: ... ) -> RegexValidator: ...
validate_comma_separated_integer_list: Any validate_comma_separated_integer_list: Any
class BaseValidator: class BaseValidator:
message: Any = ... message: str = ...
code: str = ... code: str = ...
limit_value: Any = ... 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 __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: ... def clean(self, x: Any) -> Any: ...
class MaxValueValidator(BaseValidator): class MaxValueValidator(BaseValidator): ...
message: Any = ... class MinValueValidator(BaseValidator): ...
code: str = ... class MinLengthValidator(BaseValidator): ...
def compare(self, a: Union[datetime, Decimal, float], b: Union[datetime, Decimal, float]) -> bool: ... class MaxLengthValidator(BaseValidator): ...
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 DecimalValidator: class DecimalValidator:
messages: Any = ... messages: Dict[str, str] = ...
max_digits: int = ... max_digits: int = ...
decimal_places: int = ... decimal_places: int = ...
def __init__(self, max_digits: Optional[Union[int, str]], decimal_places: Optional[Union[int, str]]) -> None: ... def __init__(self, max_digits: Optional[Union[int, str]], decimal_places: Optional[Union[int, str]]) -> None: ...
def __call__(self, value: Decimal) -> None: ... def __call__(self, value: Decimal) -> None: ...
class FileExtensionValidator: class FileExtensionValidator:
message: Any = ... message: str = ...
code: str = ... code: str = ...
allowed_extensions: List[str] = ... allowed_extensions: List[str] = ...
def __init__( def __init__(
self, self,
allowed_extensions: Optional[Collection[str]] = ..., allowed_extensions: Optional[Collection[str]] = ...,
message: Optional[str] = ..., message: Optional[_ErrorMessage] = ...,
code: Optional[str] = ..., code: Optional[str] = ...,
) -> None: ... ) -> None: ...
def __call__(self, value: File) -> None: ... def __call__(self, value: File) -> None: ...
@@ -130,7 +115,7 @@ def get_available_image_extensions() -> List[str]: ...
def validate_image_file_extension(value: File) -> None: ... def validate_image_file_extension(value: File) -> None: ...
class ProhibitNullCharactersValidator: class ProhibitNullCharactersValidator:
message: Any = ... message: str = ...
code: str = ... code: str = ...
def __init__(self, message: Optional[str] = ..., code: Optional[str] = ...) -> None: ... def __init__(self, message: Optional[_ErrorMessage] = ..., code: Optional[str] = ...) -> None: ...
def __call__(self, value: Optional[Union[Dict[Any, Any], str, UUID]]) -> None: ... def __call__(self, value: Any) -> None: ...

View File

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

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.apps.registry import Apps
from django.db.models.base import Model from django.db.models.base import Model
from django.db.models.manager import Manager from django.db.models.manager import Manager
from django.db.models.fields import Field from django.db.models.fields import Field
class AppConfigStub: class AppConfigStub(AppConfig): ...
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 ModelState: class ModelState:
name: str name: str
@@ -66,13 +58,7 @@ class ProjectState:
def remove_model(self, app_label: str, model_name: str) -> None: ... def remove_model(self, app_label: str, model_name: str) -> None: ...
class StateApps(Apps): class StateApps(Apps):
all_models: DefaultDict
apps_ready: bool
loading: bool
models_ready: bool
ready: bool
real_models: List[ModelState] real_models: List[ModelState]
stored_app_configs: List[Any]
def __init__( def __init__(
self, real_apps: List[str], models: Dict[Tuple[str, str], ModelState], ignore_swappable: bool = ... self, real_apps: List[str], models: Dict[Tuple[str, str], ModelState], ignore_swappable: bool = ...
) -> None: ... ) -> None: ...

View File

@@ -2,7 +2,7 @@ from typing import Any, Callable, Dict, List, Optional, Sequence, Set, Tuple, Ty
from django.core.checks.messages import CheckMessage from django.core.checks.messages import CheckMessage
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.db.models.manager import Manager from django.db.models.manager import BaseManager
from django.db.models.options import Options from django.db.models.options import Options
_Self = TypeVar("_Self", bound="Model") _Self = TypeVar("_Self", bound="Model")
@@ -13,9 +13,9 @@ class Model(metaclass=ModelBase):
class DoesNotExist(Exception): ... class DoesNotExist(Exception): ...
class MultipleObjectsReturned(Exception): ... class MultipleObjectsReturned(Exception): ...
class Meta: ... class Meta: ...
_default_manager: Manager[Model]
_meta: Options[Any] _meta: Options[Any]
objects: Manager[Any] _default_manager: BaseManager[Model]
objects: BaseManager[Any]
pk: Any = ... pk: Any = ...
def __init__(self: _Self, *args, **kwargs) -> None: ... def __init__(self: _Self, *args, **kwargs) -> None: ...
def delete(self, using: Any = ..., keep_parents: bool = ...) -> Tuple[int, Dict[str, int]]: ... def delete(self, using: Any = ..., keep_parents: bool = ...) -> Tuple[int, Dict[str, int]]: ...
@@ -41,7 +41,7 @@ class Model(metaclass=ModelBase):
using: Optional[str] = ..., using: Optional[str] = ...,
update_fields: Optional[Union[Sequence[str], 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]: ... def get_deferred_fields(self) -> Set[str]: ...
@classmethod @classmethod
def check(cls, **kwargs: Any) -> List[CheckMessage]: ... 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 from django.db.models.base import Model
@@ -18,4 +18,11 @@ class ProtectedError(IntegrityError): ...
class Collector: class Collector:
def __init__(self, using: str) -> None: ... 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: ... def can_fast_delete(self, objs: Union[Model, Iterable[Model]], from_field: Optional[Field] = ...) -> bool: ...

View File

@@ -27,6 +27,8 @@ from django.forms import Field as FormField, Widget
class NOT_PROVIDED: ... class NOT_PROVIDED: ...
BLANK_CHOICE_DASH: List[Tuple[str, str]] = ...
_Choice = Tuple[Any, Any] _Choice = Tuple[Any, Any]
_ChoiceNamedGroup = Tuple[str, Iterable[_Choice]] _ChoiceNamedGroup = Tuple[str, Iterable[_Choice]]
_FieldChoices = Iterable[Union[_Choice, _ChoiceNamedGroup]] _FieldChoices = Iterable[Union[_Choice, _ChoiceNamedGroup]]
@@ -63,7 +65,7 @@ class Field(RegisterLookupMixin, Generic[_ST, _GT]):
null: bool = ... null: bool = ...
editable: bool = ... editable: bool = ...
empty_strings_allowed: bool = ... empty_strings_allowed: bool = ...
choices: Optional[_FieldChoices] = ... choices: _FieldChoices = ...
db_column: Optional[str] db_column: Optional[str]
column: str column: str
default: Any default: Any
@@ -106,7 +108,10 @@ class Field(RegisterLookupMixin, Generic[_ST, _GT]):
def set_attributes_from_name(self, name: str) -> None: ... def set_attributes_from_name(self, name: str) -> None: ...
def db_type(self, connection: Any) -> str: ... def db_type(self, connection: Any) -> str: ...
def db_parameters(self, connection: Any) -> Dict[str, str]: ... def db_parameters(self, connection: Any) -> Dict[str, str]: ...
def pre_save(self, model_instance: Model, add: bool) -> Any: ...
def get_prep_value(self, value: Any) -> Any: ... def get_prep_value(self, value: Any) -> Any: ...
def get_db_prep_value(self, value: Any, connection: Any, prepared: bool) -> Any: ...
def get_db_prep_save(self, value: Any, connection: Any) -> Any: ...
def get_internal_type(self) -> str: ... def get_internal_type(self) -> str: ...
# TODO: plugin support # TODO: plugin support
def formfield(self, **kwargs) -> Any: ... def formfield(self, **kwargs) -> Any: ...

View File

@@ -1,5 +1,5 @@
from pathlib import Path from pathlib import Path
from typing import Any, Callable, Iterable, List, Optional, Tuple, Type, TypeVar, Union, overload from typing import Any, Callable, Iterable, Optional, Type, TypeVar, Union, overload
from django.core.files.base import File from django.core.files.base import File
from django.core.files.images import ImageFile from django.core.files.images import ImageFile
@@ -8,8 +8,6 @@ from django.db.models.base import Model
from django.db.models.fields import Field, _FieldChoices, _ValidatorCallable, _ErrorMessagesToOverride from django.db.models.fields import Field, _FieldChoices, _ValidatorCallable, _ErrorMessagesToOverride
BLANK_CHOICE_DASH: List[Tuple[str, str]] = ...
class FieldFile(File): class FieldFile(File):
instance: Model = ... instance: Model = ...
field: FileField = ... field: FileField = ...

View File

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

View File

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

View File

@@ -55,3 +55,11 @@ class Substr(Func):
class Trim(Transform): ... class Trim(Transform): ...
class Upper(Transform): ... class Upper(Transform): ...
class Reverse(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

@@ -13,6 +13,7 @@ class BaseManager(QuerySet[_T]):
name: str = ... name: str = ...
model: Type[_T] = ... model: Type[_T] = ...
db: str db: str
_db: Optional[str]
def __init__(self) -> None: ... def __init__(self) -> None: ...
def deconstruct(self) -> Tuple[bool, str, None, Tuple, Dict[str, int]]: ... def deconstruct(self) -> Tuple[bool, str, None, Tuple, Dict[str, int]]: ...
def check(self, **kwargs: Any) -> List[Any]: ... def check(self, **kwargs: Any) -> List[Any]: ...

View File

@@ -8,6 +8,7 @@ from django.contrib.postgres.fields.array import ArrayField
from django.contrib.postgres.fields.citext import CIText from django.contrib.postgres.fields.citext import CIText
from django.db.backends.sqlite3.base import DatabaseWrapper from django.db.backends.sqlite3.base import DatabaseWrapper
from django.db.models.base import Model 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.mixins import FieldCacheMixin
from django.db.models.fields.related import ManyToManyField, OneToOneField from django.db.models.fields.related import ManyToManyField, OneToOneField
from django.db.models.fields.reverse_related import ForeignObjectRel from django.db.models.fields.reverse_related import ForeignObjectRel
@@ -34,6 +35,7 @@ _M = TypeVar("_M", bound=Model)
class Options(Generic[_M]): class Options(Generic[_M]):
base_manager: Manager base_manager: Manager
concrete_fields: ImmutableList concrete_fields: ImmutableList
constraints: List[BaseConstraint]
default_manager: Manager default_manager: Manager
fields: ImmutableList fields: ImmutableList
local_concrete_fields: ImmutableList local_concrete_fields: ImmutableList

View File

@@ -16,6 +16,7 @@ from typing import (
TypeVar, TypeVar,
Union, Union,
overload, overload,
Reversible,
) )
from django.db.models.base import Model from django.db.models.base import Model
@@ -30,6 +31,7 @@ _T = TypeVar("_T", bound=models.Model, covariant=True)
_QS = TypeVar("_QS", bound="_BaseQuerySet") _QS = TypeVar("_QS", bound="_BaseQuerySet")
class _BaseQuerySet(Generic[_T], Sized): class _BaseQuerySet(Generic[_T], Sized):
model: Type[_T]
query: Query query: Query
def __init__( def __init__(
self, self,
@@ -121,13 +123,14 @@ class _BaseQuerySet(Generic[_T], Sized):
def db(self) -> str: ... def db(self) -> str: ...
def resolve_expression(self, *args: Any, **kwargs: Any) -> Any: ... def resolve_expression(self, *args: Any, **kwargs: Any) -> Any: ...
class QuerySet(_BaseQuerySet[_T], Collection[_T], Sized): class QuerySet(_BaseQuerySet[_T], Collection[_T], Reversible[_T], Sized):
def __iter__(self) -> Iterator[_T]: ... def __iter__(self) -> Iterator[_T]: ...
def __contains__(self, x: object) -> bool: ... def __contains__(self, x: object) -> bool: ...
@overload @overload
def __getitem__(self, i: int) -> _T: ... def __getitem__(self, i: int) -> _T: ...
@overload @overload
def __getitem__(self: _QS, s: slice) -> _QS: ... def __getitem__(self: _QS, s: slice) -> _QS: ...
def __reversed__(self) -> Iterator[_T]: ...
_Row = TypeVar("_Row", covariant=True) _Row = TypeVar("_Row", covariant=True)

View File

@@ -39,4 +39,11 @@ def atomic(using: _C) -> _C: ...
# Decorator or context-manager with parameters # Decorator or context-manager with parameters
@overload @overload
def atomic(using: Optional[str] = ..., savepoint: bool = ...) -> Atomic: ... def atomic(using: Optional[str] = ..., savepoint: bool = ...) -> Atomic: ...
def non_atomic_requests(using: Callable = ...) -> Callable: ...
# Bare decorator
@overload
def non_atomic_requests(using: _C) -> _C: ...
# Decorator with arguments
@overload
def non_atomic_requests(using: Optional[str] = ...) -> Callable[[_C], _C]: ...

View File

@@ -14,7 +14,7 @@ class Field:
initial: Any initial: Any
label: Optional[str] label: Optional[str]
required: bool required: bool
widget: Widget = ... widget: Type[Widget] = ...
hidden_widget: Any = ... hidden_widget: Any = ...
default_validators: Any = ... default_validators: Any = ...
default_error_messages: Any = ... default_error_messages: Any = ...
@@ -205,10 +205,9 @@ class CallableChoiceIterator:
def __iter__(self) -> None: ... def __iter__(self) -> None: ...
class ChoiceField(Field): class ChoiceField(Field):
choices: Any = ...
def __init__( def __init__(
self, self,
choices: Any = ..., choices: Union[_FieldChoices, Callable[[], _FieldChoices]] = ...,
required: bool = ..., required: bool = ...,
widget: Optional[Union[Widget, Type[Widget]]] = ..., widget: Optional[Union[Widget, Type[Widget]]] = ...,
label: Optional[Any] = ..., label: Optional[Any] = ...,

View File

@@ -1,5 +1,21 @@
from datetime import datetime from datetime import datetime
from typing import Any, Callable, Dict, Iterator, List, Mapping, MutableMapping, Optional, Sequence, Tuple, Type, Union from typing import (
Any,
Callable,
Dict,
Iterator,
List,
Mapping,
MutableMapping,
Optional,
Sequence,
Tuple,
Type,
Union,
ClassVar,
Container,
TypeVar,
)
from unittest.mock import MagicMock from unittest.mock import MagicMock
from uuid import UUID from uuid import UUID
@@ -24,6 +40,11 @@ _Fields = Union[List[Union[Callable, str]], Sequence[str], Literal["__all__"]]
_Labels = Dict[str, str] _Labels = Dict[str, str]
_ErrorMessages = Dict[str, Dict[str, str]] _ErrorMessages = Dict[str, Dict[str, str]]
_M = TypeVar("_M", bound=Model)
def construct_instance(
form: BaseForm, instance: _M, fields: Optional[Container[str]] = ..., exclude: Optional[Container[str]] = ...
) -> _M: ...
def model_to_dict( def model_to_dict(
instance: Model, fields: Optional[_Fields] = ..., exclude: Optional[_Fields] = ... instance: Model, fields: Optional[_Fields] = ..., exclude: Optional[_Fields] = ...
) -> Dict[str, Any]: ... ) -> Dict[str, Any]: ...
@@ -76,7 +97,8 @@ class BaseModelForm(BaseForm):
save_m2m: Any = ... save_m2m: Any = ...
def save(self, commit: bool = ...) -> Any: ... def save(self, commit: bool = ...) -> Any: ...
class ModelForm(BaseModelForm): ... class ModelForm(BaseModelForm, metaclass=ModelFormMetaclass):
base_fields: ClassVar[Dict[str, Field]] = ...
def modelform_factory( def modelform_factory(
model: Type[Model], model: Type[Model],

View File

@@ -18,6 +18,7 @@ from typing import (
from django.contrib.auth.base_user import AbstractBaseUser from django.contrib.auth.base_user import AbstractBaseUser
from django.contrib.sessions.backends.base import SessionBase 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.utils.datastructures import CaseInsensitiveMapping, ImmutableList, MultiValueDict
from django.core.files import uploadedfile, uploadhandler from django.core.files import uploadedfile, uploadhandler
@@ -51,6 +52,7 @@ class HttpRequest(BytesIO):
content_type: Optional[str] = ... content_type: Optional[str] = ...
content_params: Optional[Dict[str, str]] = ... content_params: Optional[Dict[str, str]] = ...
user: AbstractBaseUser user: AbstractBaseUser
site: Site
session: SessionBase session: SessionBase
encoding: Optional[str] = ... encoding: Optional[str] = ...
upload_handlers: UploadHandlerList = ... upload_handlers: UploadHandlerList = ...

View File

@@ -62,28 +62,27 @@ class HttpResponseBase(Iterable[Any]):
def __iter__(self) -> Iterator[Any]: ... def __iter__(self) -> Iterator[Any]: ...
class HttpResponse(HttpResponseBase): class HttpResponse(HttpResponseBase):
client: Client content: Any
context: Context
csrf_cookie_set: bool csrf_cookie_set: bool
redirect_chain: List[Tuple[str, int]] redirect_chain: List[Tuple[str, int]]
request: Dict[str, Any]
resolver_match: ResolverMatch
sameorigin: bool sameorigin: bool
templates: List[Template]
test_server_port: str test_server_port: str
test_was_secure_request: bool test_was_secure_request: bool
wsgi_request: WSGIRequest
xframe_options_exempt: bool xframe_options_exempt: bool
streaming: bool = ... streaming: bool = ...
def __init__(self, content: object = ..., *args: Any, **kwargs: Any) -> None: ... def __init__(self, content: object = ..., *args: Any, **kwargs: Any) -> None: ...
def serialize(self) -> bytes: ... def serialize(self) -> bytes: ...
@property @property
def content(self) -> Any: ...
@content.setter
def content(self, value: Any) -> None: ...
@property
def url(self) -> str: ... def url(self) -> str: ...
def json(self) -> Dict[str, Any]: ... # Attributes assigned by monkey-patching in test client ClientHandler.__call__()
wsgi_request: WSGIRequest
# Attributes assigned by monkey-patching in test client Client.request()
client: Client
request: Dict[str, Any]
templates: List[Template]
context: Context
resolver_match: ResolverMatch
def json(self) -> Any: ...
class StreamingHttpResponse(HttpResponseBase): class StreamingHttpResponse(HttpResponseBase):
content: Any content: Any
@@ -107,7 +106,7 @@ class FileResponse(StreamingHttpResponse):
def json(self) -> Dict[str, Any]: ... def json(self) -> Dict[str, Any]: ...
class HttpResponseRedirectBase(HttpResponse): class HttpResponseRedirectBase(HttpResponse):
allowed_schemes = ... # type: List[str] allowed_schemes: List[str] = ...
def __init__(self, redirect_to: str, *args: Any, **kwargs: Any) -> None: ... def __init__(self, redirect_to: str, *args: Any, **kwargs: Any) -> None: ...
class HttpResponseRedirect(HttpResponseRedirectBase): ... class HttpResponseRedirect(HttpResponseRedirectBase): ...

View File

@@ -1,15 +1,16 @@
from io import BytesIO from io import BytesIO
from types import TracebackType
from typing import Any, Dict, List, Optional, Pattern, Tuple, Type, Union from typing import Any, Dict, List, Optional, Pattern, Tuple, Type, Union
from django.contrib.auth.models import AbstractUser from django.contrib.auth.models import AbstractUser
from django.contrib.sessions.backends.base import SessionBase from django.contrib.sessions.backends.base import SessionBase
from django.core.handlers.base import BaseHandler from django.core.handlers.base import BaseHandler
from django.core.serializers.json import DjangoJSONEncoder
from django.http.cookie import SimpleCookie from django.http.cookie import SimpleCookie
from django.http.request import HttpRequest from django.http.request import HttpRequest
from django.http.response import HttpResponse, HttpResponseBase from django.http.response import HttpResponse, HttpResponseBase
from django.core.handlers.wsgi import WSGIRequest from django.core.handlers.wsgi import WSGIRequest
from json import JSONEncoder
BOUNDARY: str = ... BOUNDARY: str = ...
MULTIPART_CONTENT: str = ... MULTIPART_CONTENT: str = ...
@@ -37,11 +38,11 @@ def encode_multipart(boundary: str, data: Dict[str, Any]) -> bytes: ...
def encode_file(boundary: str, key: str, file: Any) -> List[bytes]: ... def encode_file(boundary: str, key: str, file: Any) -> List[bytes]: ...
class RequestFactory: class RequestFactory:
json_encoder: Type[DjangoJSONEncoder] = ... json_encoder: Type[JSONEncoder]
defaults: Dict[str, str] = ... defaults: Dict[str, str]
cookies: SimpleCookie = ... cookies: SimpleCookie
errors: BytesIO = ... errors: BytesIO
def __init__(self, *, json_encoder: Any = ..., **defaults: Any) -> None: ... def __init__(self, *, json_encoder: Type[JSONEncoder] = ..., **defaults: Any) -> None: ...
def request(self, **request: Any) -> WSGIRequest: ... def request(self, **request: Any) -> WSGIRequest: ...
def get(self, path: str, data: Any = ..., secure: bool = ..., **extra: Any) -> WSGIRequest: ... def get(self, path: str, data: Any = ..., secure: bool = ..., **extra: Any) -> WSGIRequest: ...
def post( def post(
@@ -54,6 +55,7 @@ class RequestFactory:
path: str, path: str,
data: Union[Dict[str, str], str] = ..., data: Union[Dict[str, str], str] = ...,
content_type: str = ..., content_type: str = ...,
follow: bool = ...,
secure: bool = ..., secure: bool = ...,
**extra: Any **extra: Any
) -> WSGIRequest: ... ) -> WSGIRequest: ...
@@ -76,39 +78,50 @@ class RequestFactory:
**extra: Any **extra: Any
) -> WSGIRequest: ... ) -> WSGIRequest: ...
class Client: class Client(RequestFactory):
json_encoder: Type[DjangoJSONEncoder] = ... handler: ClientHandler
defaults: Dict[str, str] = ... raise_request_exception: bool
cookies: SimpleCookie = ... exc_info: Optional[Tuple[Type[BaseException], BaseException, TracebackType]]
errors: BytesIO = ... def __init__(
handler: ClientHandler = ... self,
exc_info: None = ... enforce_csrf_checks: bool = ...,
def __init__(self, enforce_csrf_checks: bool = ..., **defaults: Any) -> None: ... raise_request_exception: bool = ...,
def request(self, **request: Any) -> Any: ... *,
def get(self, path: str, data: Any = ..., secure: bool = ..., **extra: Any) -> Any: ... json_encoder: Type[JSONEncoder] = ...,
def post(self, path: str, data: Any = ..., content_type: str = ..., secure: bool = ..., **extra: Any) -> Any: ... **defaults: Any
def head(self, path: str, data: Any = ..., secure: bool = ..., **extra: Any) -> Any: ... ) -> None: ...
def trace(self, path: str, secure: bool = ..., **extra: Any) -> Any: ... # Silence type warnings, since this class overrides arguments and return types in an unsafe manner.
def options( def request(self, **request: Any) -> HttpResponse: ... # type: ignore
def get( # type: ignore
self, path: str, data: Any = ..., follow: bool = ..., secure: bool = ..., **extra: Any
) -> HttpResponse: ... # type: ignore
def post( # type: ignore
self, path: str, data: Any = ..., content_type: str = ..., follow: bool = ..., secure: bool = ..., **extra: Any
) -> HttpResponse: ... # type: ignore
def head( # type: ignore
self, path: str, data: Any = ..., follow: bool = ..., secure: bool = ..., **extra: Any
) -> HttpResponse: ... # type: ignore
def trace( # type: ignore
self, path: str, follow: bool = ..., secure: bool = ..., **extra: Any
) -> HttpResponse: ... # type: ignore
def options( # type: ignore
self, self,
path: str, path: str,
data: Union[Dict[str, str], str] = ..., data: Union[Dict[str, str], str] = ...,
content_type: str = ..., content_type: str = ...,
follow: bool = ...,
secure: bool = ..., secure: bool = ...,
**extra: Any **extra: Any
) -> Any: ... ) -> HttpResponse: ... # type: ignore
def put(self, path: str, data: Any = ..., content_type: str = ..., secure: bool = ..., **extra: Any) -> Any: ... def put( # type: ignore
def patch(self, path: str, data: Any = ..., content_type: str = ..., secure: bool = ..., **extra: Any) -> Any: ... self, path: str, data: Any = ..., content_type: str = ..., follow: bool = ..., secure: bool = ..., **extra: Any
def delete(self, path: str, data: Any = ..., content_type: str = ..., secure: bool = ..., **extra: Any) -> Any: ... ) -> HttpResponse: ... # type: ignore
def generic( def patch( # type: ignore
self, self, path: str, data: Any = ..., content_type: str = ..., follow: bool = ..., secure: bool = ..., **extra: Any
method: str, ) -> HttpResponse: ... # type: ignore
path: str, def delete( # type: ignore
data: Any = ..., self, path: str, data: Any = ..., content_type: str = ..., follow: bool = ..., secure: bool = ..., **extra: Any
content_type: Optional[str] = ..., ) -> HttpResponse: ... # type: ignore
secure: bool = ...,
**extra: Any
) -> Any: ...
def store_exc_info(self, **kwargs: Any) -> None: ... def store_exc_info(self, **kwargs: Any) -> None: ...
@property @property
def session(self) -> SessionBase: ... def session(self) -> SessionBase: ...

View File

@@ -1,7 +1,7 @@
import threading import threading
import unittest import unittest
from datetime import date from datetime import date
from typing import Any, Callable, Dict, Iterator, List, Optional, Set, Tuple, Type, Union, ClassVar from typing import Any, Callable, Dict, Iterator, List, Optional, Set, Tuple, Type, Union, ClassVar, overload
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.core.handlers.wsgi import WSGIHandler from django.core.handlers.wsgi import WSGIHandler
@@ -146,9 +146,14 @@ class TransactionTestCase(SimpleTestCase):
ordered: bool = ..., ordered: bool = ...,
msg: Optional[str] = ..., msg: Optional[str] = ...,
) -> None: ... ) -> None: ...
@overload
def assertNumQueries( def assertNumQueries(
self, num: int, func: Optional[Union[Callable, Type[list]]] = ..., *args: Any, using: Any = ..., **kwargs: Any self, num: int, func: Callable[..., Any], *args: Any, using: str = ..., **kwargs: Any
) -> Optional[_AssertNumQueriesContext]: ... ) -> None: ...
@overload
def assertNumQueries(
self, num: int, func: None = ..., *args: Any, using: str = ..., **kwargs: Any
) -> _AssertNumQueriesContext: ...
class TestCase(TransactionTestCase): class TestCase(TransactionTestCase):
@classmethod @classmethod

View File

@@ -16,6 +16,7 @@ from typing import (
Type, Type,
Union, Union,
ContextManager, ContextManager,
TypeVar,
) )
from django.apps.registry import Apps from django.apps.registry import Apps
@@ -29,6 +30,7 @@ from django.conf import LazySettings, Settings
_TestClass = Type[SimpleTestCase] _TestClass = Type[SimpleTestCase]
_DecoratedTest = Union[Callable, _TestClass] _DecoratedTest = Union[Callable, _TestClass]
_C = TypeVar("_C", bound=Callable) # Any callable
TZ_SUPPORT: bool = ... TZ_SUPPORT: bool = ...
@@ -56,7 +58,7 @@ class TestContextDecorator:
def __enter__(self) -> Optional[Apps]: ... def __enter__(self) -> Optional[Apps]: ...
def __exit__(self, exc_type: None, exc_value: None, traceback: None) -> None: ... def __exit__(self, exc_type: None, exc_value: None, traceback: None) -> None: ...
def decorate_class(self, cls: _TestClass) -> _TestClass: ... def decorate_class(self, cls: _TestClass) -> _TestClass: ...
def decorate_callable(self, func: Callable) -> Callable: ... def decorate_callable(self, func: _C) -> _C: ...
def __call__(self, decorated: _DecoratedTest) -> Any: ... def __call__(self, decorated: _DecoratedTest) -> Any: ...
class override_settings(TestContextDecorator): class override_settings(TestContextDecorator):
@@ -146,7 +148,7 @@ def get_unique_databases_and_mirrors() -> Tuple[Dict[_Signature, _TestDatabase],
def teardown_databases( def teardown_databases(
old_config: Iterable[Tuple[Any, str, bool]], verbosity: int, parallel: int = ..., keepdb: bool = ... old_config: Iterable[Tuple[Any, str, bool]], verbosity: int, parallel: int = ..., keepdb: bool = ...
) -> None: ... ) -> None: ...
def require_jinja2(test_func: Callable) -> Callable: ... def require_jinja2(test_func: _C) -> _C: ...
@contextmanager @contextmanager
def register_lookup( def register_lookup(
field: Type[RegisterLookupMixin], *lookups: Type[Union[Lookup, Transform]], lookup_name: Optional[str] = ... field: Type[RegisterLookupMixin], *lookups: Type[Union[Lookup, Transform]], lookup_name: Optional[str] = ...

View File

@@ -1,7 +1,6 @@
from typing import Any, Optional, Tuple from typing import Any, Optional, Tuple
from django.core.cache.backends.base import BaseCache from django.core.cache.backends.base import BaseCache
from django.core.cache.backends.locmem import LocMemCache
from django.core.handlers.wsgi import WSGIRequest from django.core.handlers.wsgi import WSGIRequest
from django.http.response import HttpResponse, HttpResponseBase from django.http.response import HttpResponse, HttpResponseBase
@@ -28,5 +27,5 @@ def learn_cache_key(
response: HttpResponse, response: HttpResponse,
cache_timeout: Optional[float] = ..., cache_timeout: Optional[float] = ...,
key_prefix: Optional[str] = ..., key_prefix: Optional[str] = ...,
cache: Optional[LocMemCache] = ..., cache: Optional[BaseCache] = ...,
) -> str: ... ) -> str: ...

View File

@@ -1,13 +1,16 @@
from typing import Any, Callable, Iterable, Optional, Type, Union from typing import Any, Callable, Iterable, Optional, Type, Union, TypeVar
from django.utils.deprecation import MiddlewareMixin from django.utils.deprecation import MiddlewareMixin
from django.views.generic.base import View
_T = TypeVar("_T", bound=Union[View, Callable]) # Any callable
class classonlymethod(classmethod): ... class classonlymethod(classmethod): ...
def method_decorator(decorator: Union[Callable, Iterable[Callable]], name: str = ...) -> Callable: ... def method_decorator(decorator: Union[Callable, Iterable[Callable]], name: str = ...) -> Callable[[_T], _T]: ...
def decorator_from_middleware_with_args(middleware_class: type) -> Callable: ... def decorator_from_middleware_with_args(middleware_class: type) -> Callable: ...
def decorator_from_middleware(middleware_class: type) -> Callable: ... def decorator_from_middleware(middleware_class: type) -> Callable: ...
def available_attrs(fn: Any): ... def available_attrs(fn: Callable): ...
def make_middleware_decorator(middleware_class: Type[MiddlewareMixin]) -> Callable: ... def make_middleware_decorator(middleware_class: Type[MiddlewareMixin]) -> Callable: ...
class classproperty: class classproperty:

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): class DjangoUnicodeDecodeError(UnicodeDecodeError):
obj: bytes = ... obj: bytes = ...
def __init__(self, obj: bytes, *args: Any) -> None: ... 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 smart_text(s: Any, encoding: str = ..., strings_only: bool = ..., errors: str = ...) -> str: ...
def is_protected_type(obj: Any) -> bool: ... 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: ... 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: ... def force_bytes(s: Any, encoding: str = ..., strings_only: bool = ..., errors: str = ...) -> bytes: ...
smart_str = smart_text smart_str = smart_text
force_str = force_text force_str = force_text
@overload
def iri_to_uri(iri: Optional[str]) -> Optional[str]: ... def iri_to_uri(iri: None) -> None: ...
def uri_to_iri(uri: Optional[str]) -> Optional[str]: ... @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 escape_uri_path(path: str) -> str: ...
def repercent_broken_unicode(path: bytes) -> bytes: ... 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: ... def get_system_encoding() -> str: ...
DEFAULT_LOCALE_ENCODING: Any DEFAULT_LOCALE_ENCODING: Any

View File

@@ -1,16 +1,21 @@
from typing import Any, Callable, Dict, List, Optional, Tuple, Type, Union from typing import Any, Callable, Dict, List, Optional, Tuple, Type, Union, TypeVar, Generic, overload
from functools import wraps as wraps # noqa: F401 from functools import wraps as wraps # noqa: F401
from django.db.models.base import Model from django.db.models.base import Model
def curry(_curried_func: Any, *args: Any, **kwargs: Any): ... def curry(_curried_func: Any, *args: Any, **kwargs: Any): ...
class cached_property: _T = TypeVar("_T")
func: Callable = ...
class cached_property(Generic[_T]):
func: Callable[..., _T] = ...
__doc__: Any = ... __doc__: Any = ...
name: str = ... name: str = ...
def __init__(self, func: Callable, name: Optional[str] = ...) -> None: ... def __init__(self, func: Callable[..., _T], name: Optional[str] = ...): ...
def __get__(self, instance: Any, cls: Type[Any] = ...) -> Any: ... @overload
def __get__(self, instance: None, cls: Type[Any] = ...) -> "cached_property[_T]": ...
@overload
def __get__(self, instance: object, cls: Type[Any] = ...) -> _T: ...
class Promise: ... class Promise: ...

View File

@@ -10,7 +10,7 @@ class Node:
connector: str = ... connector: str = ...
negated: bool = ... negated: bool = ...
def __init__( def __init__(
self, children: Optional[_NodeChildren] = ..., connector: Optional[str] = ..., negated: bool = ..., self, children: Optional[_NodeChildren] = ..., connector: Optional[str] = ..., negated: bool = ...
) -> None: ... ) -> None: ...
def __deepcopy__(self, memodict: Dict[Any, Any]) -> Node: ... def __deepcopy__(self, memodict: Dict[Any, Any]) -> Node: ...
def __len__(self) -> int: ... def __len__(self) -> int: ...

View File

@@ -1,11 +1,12 @@
from typing import Any, Callable, Dict, Optional, Sequence, Type, Union from typing import Any, Callable, Dict, Optional, Sequence, Type, Union
from django.forms.forms import BaseForm from django.forms.forms import BaseForm
from django.http import HttpRequest, HttpResponse
from django.views.generic.base import ContextMixin, TemplateResponseMixin, View from django.views.generic.base import ContextMixin, TemplateResponseMixin, View
from django.views.generic.detail import BaseDetailView, SingleObjectMixin, SingleObjectTemplateResponseMixin from django.views.generic.detail import BaseDetailView, SingleObjectMixin, SingleObjectTemplateResponseMixin
from typing_extensions import Literal from typing_extensions import Literal
from django.http import HttpRequest, HttpResponse
class FormMixin(ContextMixin): class FormMixin(ContextMixin):
initial: Dict[str, Any] = ... initial: Dict[str, Any] = ...
form_class: Optional[Type[BaseForm]] = ... form_class: Optional[Type[BaseForm]] = ...

View File

@@ -1 +1,2 @@
[mypy] [mypy]
warn_unused_ignores = True

View File

@@ -124,7 +124,7 @@ class DjangoContext:
if isinstance(field, ForeignObjectRel): if isinstance(field, ForeignObjectRel):
yield field 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)): if isinstance(field, (RelatedField, ForeignObjectRel)):
related_model_cls = field.related_model related_model_cls = field.related_model
primary_key_field = self.get_primary_key_field(related_model_cls) primary_key_field = self.get_primary_key_field(related_model_cls)
@@ -134,10 +134,8 @@ class DjangoContext:
if rel_model_info is None: if rel_model_info is None:
return AnyType(TypeOfAny.explicit) return AnyType(TypeOfAny.explicit)
model_and_primary_key_type = UnionType.make_union([Instance(rel_model_info, []), model_and_primary_key_type = UnionType.make_union([Instance(rel_model_info, []), primary_key_type])
primary_key_type])
return helpers.make_optional(model_and_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__) field_info = helpers.lookup_class_typeinfo(api, field.__class__)
if field_info is None: if field_info is None:
@@ -228,21 +226,22 @@ class DjangoContext:
attname = field.attname attname = field.attname
return attname return attname
def get_field_nullability(self, field: Field, method: Optional[str]) -> bool: def get_field_nullability(self, field: Union[Field, ForeignObjectRel], method: Optional[str]) -> bool:
nullable = field.null nullable = field.null
if not nullable and isinstance(field, CharField) and field.blank: if not nullable and isinstance(field, CharField) and field.blank:
return True return True
if method == '__init__': if method == '__init__':
if field.primary_key or isinstance(field, ForeignKey): if ((isinstance(field, Field) and field.primary_key)
or isinstance(field, ForeignKey)):
return True return True
if method == 'create': if method == 'create':
if isinstance(field, AutoField): if isinstance(field, AutoField):
return True return True
if field.has_default(): if isinstance(field, Field) and field.has_default():
return True return True
return nullable return nullable
def get_field_set_type(self, api: TypeChecker, field: Field, *, method: str) -> MypyType: 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. """ """ Get a type of __set__ for this specific Django field. """
target_field = field target_field = field
if isinstance(field, ForeignKey): if isinstance(field, ForeignKey):
@@ -259,7 +258,7 @@ class DjangoContext:
field_set_type = helpers.convert_any_to_type(field_set_type, argument_field_type) field_set_type = helpers.convert_any_to_type(field_set_type, argument_field_type)
return field_set_type return field_set_type
def get_field_get_type(self, api: TypeChecker, field: Field, *, method: str) -> MypyType: 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. """ """ Get a type of __get__ for this specific Django field. """
field_info = helpers.lookup_class_typeinfo(api, field.__class__) field_info = helpers.lookup_class_typeinfo(api, field.__class__)
if field_info is None: if field_info is None:
@@ -303,7 +302,10 @@ class DjangoContext:
return related_model_cls return related_model_cls
def _resolve_field_from_parts(self, field_parts: Iterable[str], model_cls: Type[Model]) -> Field: def _resolve_field_from_parts(self,
field_parts: Iterable[str],
model_cls: Type[Model]
) -> Union[Field, ForeignObjectRel]:
currently_observed_model = model_cls currently_observed_model = model_cls
field = None field = None
for field_part in field_parts: for field_part in field_parts:
@@ -325,7 +327,7 @@ class DjangoContext:
assert field is not None assert field is not None
return field return field
def resolve_lookup_into_field(self, model_cls: Type[Model], lookup: str) -> Field: def resolve_lookup_into_field(self, model_cls: Type[Model], lookup: str) -> Union[Field, ForeignObjectRel]:
query = Query(model_cls) query = Query(model_cls)
lookup_parts, field_parts, is_expression = query.solve_lookup_type(lookup) lookup_parts, field_parts, is_expression = query.solve_lookup_type(lookup)
if lookup_parts: if lookup_parts:

View File

@@ -13,7 +13,7 @@ DUMMY_SETTINGS_BASE_CLASS = 'django.conf._DjangoConfLazyObject'
QUERYSET_CLASS_FULLNAME = 'django.db.models.query.QuerySet' QUERYSET_CLASS_FULLNAME = 'django.db.models.query.QuerySet'
BASE_MANAGER_CLASS_FULLNAME = 'django.db.models.manager.BaseManager' BASE_MANAGER_CLASS_FULLNAME = 'django.db.models.manager.BaseManager'
MANAGER_CLASS_FULLNAME = 'django.db.models.manager.Manager' 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' BASEFORM_CLASS_FULLNAME = 'django.forms.forms.BaseForm'
FORM_CLASS_FULLNAME = 'django.forms.forms.Form' FORM_CLASS_FULLNAME = 'django.forms.forms.Form'
@@ -23,9 +23,7 @@ FORM_MIXIN_CLASS_FULLNAME = 'django.views.generic.edit.FormMixin'
MANAGER_CLASSES = { MANAGER_CLASSES = {
MANAGER_CLASS_FULLNAME, MANAGER_CLASS_FULLNAME,
RELATED_MANAGER_CLASS_FULLNAME,
BASE_MANAGER_CLASS_FULLNAME, BASE_MANAGER_CLASS_FULLNAME,
# QUERYSET_CLASS_FULLNAME
} }
RELATED_FIELDS_CLASSES = { RELATED_FIELDS_CLASSES = {

View File

@@ -1,6 +1,6 @@
from collections import OrderedDict from collections import OrderedDict
from typing import ( 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 from django.db.models.fields import Field
@@ -10,13 +10,15 @@ from mypy import checker
from mypy.checker import TypeChecker from mypy.checker import TypeChecker
from mypy.mro import calculate_mro from mypy.mro import calculate_mro
from mypy.nodes import ( from mypy.nodes import (
GDEF, MDEF, Block, ClassDef, Expression, MemberExpr, MypyFile, NameExpr, StrExpr, SymbolNode, SymbolTable, GDEF, MDEF, Argument, Block, ClassDef, Expression, FuncDef, MemberExpr, MypyFile, NameExpr, PlaceholderNode,
SymbolTableNode, TypeInfo, Var, StrExpr, SymbolNode, SymbolTable, SymbolTableNode, TypeInfo, Var,
) )
from mypy.plugin import ( 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 Type as MypyType
from mypy.types import TypedDictType, TypeOfAny, UnionType 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 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) node = lookup_fully_qualified_generic(fullname, api.modules)
if not isinstance(node, TypeInfo): if not isinstance(node, TypeInfo):
return None return None
@@ -173,8 +175,11 @@ def get_nested_meta_node_for_current_class(info: TypeInfo) -> Optional[TypeInfo]
return None return None
def add_new_class_for_module(module: MypyFile, name: str, bases: List[Instance], def add_new_class_for_module(module: MypyFile,
fields: 'OrderedDict[str, MypyType]') -> TypeInfo: name: str,
bases: List[Instance],
fields: Optional[Dict[str, MypyType]] = None
) -> TypeInfo:
new_class_unique_name = checker.gen_unique_name(name, module.names) new_class_unique_name = checker.gen_unique_name(name, module.names)
# make new class expression # make new class expression
@@ -188,11 +193,12 @@ def add_new_class_for_module(module: MypyFile, name: str, bases: List[Instance],
new_typeinfo.calculate_metaclass_type() new_typeinfo.calculate_metaclass_type()
# add fields # add fields
for field_name, field_type in fields.items(): if fields:
var = Var(field_name, type=field_type) for field_name, field_type in fields.items():
var.info = new_typeinfo var = Var(field_name, type=field_type)
var._fullname = new_typeinfo.fullname + '.' + field_name var.info = new_typeinfo
new_typeinfo.names[field_name] = SymbolTableNode(MDEF, var, plugin_generated=True) var._fullname = new_typeinfo.fullname + '.' + field_name
new_typeinfo.names[field_name] = SymbolTableNode(MDEF, var, plugin_generated=True)
classdef.info = new_typeinfo classdef.info = new_typeinfo
module.names[new_class_unique_name] = SymbolTableNode(GDEF, new_typeinfo, plugin_generated=True) module.names[new_class_unique_name] = SymbolTableNode(GDEF, new_typeinfo, plugin_generated=True)
@@ -253,8 +259,7 @@ def make_typeddict(api: CheckerPluginInterface, fields: 'OrderedDict[str, MypyTy
return typed_dict_type return typed_dict_type
def resolve_string_attribute_value(attr_expr: Expression, ctx: Union[FunctionContext, MethodContext], def resolve_string_attribute_value(attr_expr: Expression, django_context: 'DjangoContext') -> Optional[str]:
django_context: 'DjangoContext') -> Optional[str]:
if isinstance(attr_expr, StrExpr): if isinstance(attr_expr, StrExpr):
return attr_expr.value return attr_expr.value
@@ -264,15 +269,19 @@ 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 isinstance(attr_expr.expr, NameExpr) and attr_expr.expr.fullname == 'django.conf.settings':
if hasattr(django_context.settings, member_name): if hasattr(django_context.settings, member_name):
return getattr(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 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: def get_typechecker_api(ctx: Union[AttributeContext, MethodContext, FunctionContext]) -> TypeChecker:
if not isinstance(ctx.api, TypeChecker): if not isinstance(ctx.api, TypeChecker):
raise ValueError('Not a 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: def is_model_subclass_info(info: TypeInfo, django_context: 'DjangoContext') -> bool:
@@ -298,3 +307,69 @@ def add_new_sym_for_info(info: TypeInfo, *, name: str, sym_type: MypyType) -> No
var.is_inferred = True var.is_inferred = True
info.names[name] = SymbolTableNode(MDEF, var, info.names[name] = SymbolTableNode(MDEF, var,
plugin_generated=True) plugin_generated=True)
def build_unannotated_method_args(method_node: FuncDef) -> Tuple[List[Argument], MypyType]:
prepared_arguments = []
for argument in method_node.arguments[1:]:
argument.type_annotation = AnyType(TypeOfAny.unannotated)
prepared_arguments.append(argument)
return_type = AnyType(TypeOfAny.unannotated)
return prepared_arguments, return_type
def copy_method_to_another_class(ctx: ClassDefContext, self_type: Instance,
new_method_name: str, method_node: FuncDef) -> None:
semanal_api = get_semanal_api(ctx)
if method_node.type is None:
if not semanal_api.final_iteration:
semanal_api.defer()
return
arguments, return_type = build_unannotated_method_args(method_node)
add_method(ctx,
new_method_name,
args=arguments,
return_type=return_type,
self_type=self_type)
return
method_type = method_node.type
if not isinstance(method_type, CallableType):
if not semanal_api.final_iteration:
semanal_api.defer()
return
arguments = []
bound_return_type = semanal_api.anal_type(method_type.ret_type,
allow_placeholder=True)
assert bound_return_type is not None
if isinstance(bound_return_type, PlaceholderNode):
return
for arg_name, arg_type, original_argument in zip(method_type.arg_names[1:],
method_type.arg_types[1:],
method_node.arguments[1:]):
bound_arg_type = semanal_api.anal_type(arg_type, allow_placeholder=True)
assert bound_arg_type is not None
if isinstance(bound_arg_type, PlaceholderNode):
return
var = Var(name=original_argument.variable.name,
type=arg_type)
var.line = original_argument.variable.line
var.column = original_argument.variable.column
argument = Argument(variable=var,
type_annotation=bound_arg_type,
initializer=original_argument.initializer,
kind=original_argument.kind)
argument.set_line(original_argument)
arguments.append(argument)
add_method(ctx,
new_method_name,
args=arguments,
return_type=bound_return_type,
self_type=self_type)

View File

@@ -7,7 +7,7 @@ from mypy.errors import Errors
from mypy.nodes import MypyFile, TypeInfo from mypy.nodes import MypyFile, TypeInfo
from mypy.options import Options from mypy.options import Options
from mypy.plugin import ( from mypy.plugin import (
AttributeContext, ClassDefContext, FunctionContext, MethodContext, Plugin, AttributeContext, ClassDefContext, DynamicClassDefContext, FunctionContext, MethodContext, Plugin,
) )
from mypy.types import Type as MypyType from mypy.types import Type as MypyType
@@ -17,6 +17,9 @@ from mypy_django_plugin.lib import fullnames, helpers
from mypy_django_plugin.transformers import ( from mypy_django_plugin.transformers import (
fields, forms, init_create, meta, querysets, request, settings, 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 from mypy_django_plugin.transformers.models import process_model_class
@@ -242,6 +245,15 @@ class NewSemanalDjangoPlugin(Plugin):
return partial(request.set_auth_user_model_as_type_for_request_user, django_context=self.django_context) return partial(request.set_auth_user_model_as_type_for_request_user, django_context=self.django_context)
return None 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): def plugin(version):
return NewSemanalDjangoPlugin return NewSemanalDjangoPlugin

View File

@@ -0,0 +1,77 @@
from mypy.nodes import (
GDEF, FuncDef, MemberExpr, NameExpr, RefExpr, StrExpr, SymbolTableNode, TypeInfo,
)
from mypy.plugin import ClassDefContext, DynamicClassDefContext
from mypy.types import AnyType, Instance, TypeOfAny
from mypy_django_plugin.lib import fullnames, helpers
def create_new_manager_class_from_from_queryset_method(ctx: DynamicClassDefContext) -> None:
semanal_api = helpers.get_semanal_api(ctx)
callee = ctx.call.callee
assert isinstance(callee, MemberExpr)
assert isinstance(callee.expr, RefExpr)
base_manager_info = 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, [])
# we need to copy all methods in MRO before django.db.models.query.QuerySet
for class_mro_info in derived_queryset_info.mro:
if class_mro_info.fullname == fullnames.QUERYSET_CLASS_FULLNAME:
break
for name, sym in class_mro_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

@@ -36,7 +36,7 @@ def return_proper_field_type_from_get_field(ctx: MethodContext, django_context:
if field_name_expr is None: if field_name_expr is None:
return ctx.default_return_type 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: if field_name is None:
return ctx.default_return_type return ctx.default_return_type

View File

@@ -1,5 +1,4 @@
from collections import OrderedDict from typing import Dict, List, Optional, Type, cast
from typing import List, Tuple, Type
from django.db.models.base import Model from django.db.models.base import Model
from django.db.models.fields import DateField, DateTimeField from django.db.models.fields import DateField, DateTimeField
@@ -10,8 +9,8 @@ from django.db.models.fields.reverse_related import (
from mypy.nodes import ARG_STAR2, Argument, Context, FuncDef, TypeInfo, Var from mypy.nodes import ARG_STAR2, Argument, Context, FuncDef, TypeInfo, Var
from mypy.plugin import ClassDefContext from mypy.plugin import ClassDefContext
from mypy.plugins import common from mypy.plugins import common
from mypy.plugins.common import add_method from mypy.semanal import SemanticAnalyzer
from mypy.types import AnyType, CallableType, Instance from mypy.types import AnyType, Instance
from mypy.types import Type as MypyType from mypy.types import Type as MypyType
from mypy.types import TypeOfAny from mypy.types import TypeOfAny
@@ -22,17 +21,22 @@ from mypy_django_plugin.transformers.fields import get_field_descriptor_types
class ModelClassInitializer: class ModelClassInitializer:
api: SemanticAnalyzer
def __init__(self, ctx: ClassDefContext, django_context: DjangoContext): def __init__(self, ctx: ClassDefContext, django_context: DjangoContext):
self.api = ctx.api self.api = cast(SemanticAnalyzer, ctx.api)
self.model_classdef = ctx.cls self.model_classdef = ctx.cls
self.django_context = django_context self.django_context = django_context
self.ctx = ctx 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: def lookup_typeinfo_or_incomplete_defn_error(self, fullname: str) -> TypeInfo:
sym = self.api.lookup_fully_qualified_or_none(fullname) info = self.lookup_typeinfo(fullname)
if sym is None or not isinstance(sym.node, TypeInfo): if info is None:
raise helpers.IncompleteDefnException(f'No {fullname!r} found') 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: def lookup_class_typeinfo_or_incomplete_defn_error(self, klass: type) -> TypeInfo:
fullname = helpers.get_class_fullname(klass) fullname = helpers.get_class_fullname(klass)
@@ -54,6 +58,12 @@ class ModelClassInitializer:
name=name, name=name,
sym_type=typ) sym_type=typ)
def add_new_class_for_current_module(self, name: str, bases: List[Instance]) -> TypeInfo:
current_module = self.api.modules[self.model_classdef.info.module_name]
new_class_info = helpers.add_new_class_for_module(current_module,
name=name, bases=bases)
return new_class_info
def run(self) -> None: def run(self) -> None:
model_cls = self.django_context.get_model_class_by_fullname(self.model_classdef.fullname) model_cls = self.django_context.get_model_class_by_fullname(self.model_classdef.fullname)
if model_cls is None: if model_cls is None:
@@ -118,7 +128,14 @@ class AddRelatedModelsId(ModelClassInitializer):
continue continue
rel_primary_key_field = self.django_context.get_primary_key_field(related_model_cls) rel_primary_key_field = self.django_context.get_primary_key_field(related_model_cls)
field_info = self.lookup_class_typeinfo_or_incomplete_defn_error(rel_primary_key_field.__class__) try:
field_info = self.lookup_class_typeinfo_or_incomplete_defn_error(rel_primary_key_field.__class__)
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) is_nullable = self.django_context.get_field_nullability(field, None)
set_type, get_type = get_field_descriptor_types(field_info, is_nullable) set_type, get_type = get_field_descriptor_types(field_info, is_nullable)
self.add_new_node_to_model_class(field.attname, self.add_new_node_to_model_class(field.attname,
@@ -126,78 +143,96 @@ class AddRelatedModelsId(ModelClassInitializer):
class AddManagers(ModelClassInitializer): class AddManagers(ModelClassInitializer):
def _is_manager_any(self, typ: Instance) -> bool: def has_any_parametrized_manager_as_base(self, info: TypeInfo) -> bool:
return typ.type.fullname == fullnames.MANAGER_CLASS_FULLNAME and type(typ.args[0]) == AnyType for base in helpers.iter_bases(info):
if self.is_any_parametrized_manager(base):
return True
return False
def is_any_parametrized_manager(self, typ: Instance) -> bool:
return typ.type.fullname in fullnames.MANAGER_CLASSES and isinstance(typ.args[0], AnyType)
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 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)
new_manager_info = self.add_new_class_for_current_module(name, bases)
# copy fields to a new manager
new_cls_def_context = ClassDefContext(cls=new_manager_info.defn,
reason=self.ctx.reason,
api=self.api)
custom_manager_type = Instance(new_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 = new_manager_info
new_var._fullname = new_manager_info.fullname + '.' + name
new_sym.node = new_var
new_manager_info.names[name] = new_sym
return custom_manager_type
def run_with_model_cls(self, model_cls: Type[Model]) -> None: def run_with_model_cls(self, model_cls: Type[Model]) -> None:
for manager_name, manager in model_cls._meta.managers_map.items(): 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__) manager_fullname = helpers.get_class_fullname(manager.__class__)
manager_info = self.lookup_typeinfo_or_incomplete_defn_error(manager_fullname) 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: if manager_name not in self.model_classdef.info.names:
manager_type = Instance(manager_info, [Instance(self.model_classdef.info, [])]) manager_type = Instance(manager_info, [Instance(self.model_classdef.info, [])])
self.add_new_node_to_model_class(manager_name, manager_type) self.add_new_node_to_model_class(manager_name, manager_type)
else: else:
# creates new MODELNAME_MANAGERCLASSNAME class that represents manager parametrized with current model # 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 not self.has_any_parametrized_manager_as_base(manager_info):
if has_manager_any_base: continue
custom_model_manager_name = manager.model.__name__ + '_' + manager.__class__.__name__
bases = [] custom_model_manager_name = manager.model.__name__ + '_' + manager_class_name
for original_base in manager_info.bases: try:
if self._is_manager_any(original_base): custom_manager_type = self.create_new_model_parametrized_manager(custom_model_manager_name,
if original_base.type is None: base_manager_info=manager_info)
raise helpers.IncompleteDefnException() except helpers.IncompleteDefnException:
continue
original_base = helpers.reparametrize_instance(original_base, self.add_new_node_to_model_class(manager_name, custom_manager_type)
[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())
# 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 manager_info.names.items():
# replace self type with new class, if copying method
if isinstance(sym.node, FuncDef):
arguments, return_type = self.prepare_new_method_arguments(sym.node)
add_method(new_cls_def_context,
name,
args=arguments,
return_type=return_type,
self_type=custom_manager_type)
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
self.add_new_node_to_model_class(manager_name, custom_manager_type)
def prepare_new_method_arguments(self, 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
class AddDefaultManagerAttribute(ModelClassInitializer): class AddDefaultManagerAttribute(ModelClassInitializer):
@@ -223,16 +258,44 @@ class AddRelatedManagers(ModelClassInitializer):
if related_model_cls is None: if related_model_cls is None:
continue continue
related_model_info = self.lookup_class_typeinfo_or_incomplete_defn_error(related_model_cls) 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): if isinstance(relation, OneToOneRel):
self.add_new_node_to_model_class(attname, Instance(related_model_info, [])) self.add_new_node_to_model_class(attname, Instance(related_model_info, []))
continue continue
if isinstance(relation, (ManyToOneRel, ManyToManyRel)): if isinstance(relation, (ManyToOneRel, ManyToManyRel)):
manager_info = self.lookup_typeinfo_or_incomplete_defn_error(fullnames.RELATED_MANAGER_CLASS_FULLNAME) try:
self.add_new_node_to_model_class(attname, related_manager_info = self.lookup_typeinfo_or_incomplete_defn_error(fullnames.RELATED_MANAGER_CLASS) # noqa: E501
Instance(manager_info, [Instance(related_model_info, [])])) if 'objects' not in related_model_info.names:
continue raise helpers.IncompleteDefnException()
except helpers.IncompleteDefnException as exc:
if not self.api.final_iteration:
raise exc
else:
continue
# create new RelatedManager subclass
parametrized_related_manager_type = Instance(related_manager_info,
[Instance(related_model_info, [])])
default_manager_type = related_model_info.names['objects'].type
if (default_manager_type is None
or not isinstance(default_manager_type, Instance)
or default_manager_type.type.fullname == fullnames.MANAGER_CLASS_FULLNAME):
self.add_new_node_to_model_class(attname, parametrized_related_manager_type)
continue
name = related_model_cls.__name__ + '_' + 'RelatedManager'
bases = [parametrized_related_manager_type, default_manager_type]
new_related_manager_info = self.add_new_class_for_current_module(name, bases)
self.add_new_node_to_model_class(attname, Instance(new_related_manager_info, []))
class AddExtraFieldMethods(ModelClassInitializer): class AddExtraFieldMethods(ModelClassInitializer):

View File

@@ -1,9 +1,10 @@
from collections import OrderedDict from collections import OrderedDict
from typing import List, Optional, Sequence, Type, Union from typing import List, Optional, Sequence, Type
from django.core.exceptions import FieldError from django.core.exceptions import FieldError
from django.db.models.base import Model from django.db.models.base import Model
from django.db.models.fields.related import RelatedField from django.db.models.fields.related import RelatedField
from django.db.models.fields.reverse_related import ForeignObjectRel
from mypy.nodes import Expression, NameExpr from mypy.nodes import Expression, NameExpr
from mypy.plugin import FunctionContext, MethodContext from mypy.plugin import FunctionContext, MethodContext
from mypy.types import AnyType, Instance from mypy.types import AnyType, Instance
@@ -47,7 +48,8 @@ def get_field_type_from_lookup(ctx: MethodContext, django_context: DjangoContext
except LookupsAreUnsupported: except LookupsAreUnsupported:
return AnyType(TypeOfAny.explicit) return AnyType(TypeOfAny.explicit)
if isinstance(lookup_field, RelatedField) and lookup_field.column == lookup: if ((isinstance(lookup_field, RelatedField) and lookup_field.column == lookup)
or isinstance(lookup_field, ForeignObjectRel)):
related_model_cls = django_context.get_field_related_model_cls(lookup_field) related_model_cls = django_context.get_field_related_model_cls(lookup_field)
if related_model_cls is None: if related_model_cls is None:
return AnyType(TypeOfAny.from_error) return AnyType(TypeOfAny.from_error)
@@ -60,7 +62,7 @@ def get_field_type_from_lookup(ctx: MethodContext, django_context: DjangoContext
def get_values_list_row_type(ctx: MethodContext, django_context: DjangoContext, model_cls: Type[Model], def get_values_list_row_type(ctx: MethodContext, django_context: DjangoContext, model_cls: Type[Model],
flat: bool, named: bool) -> MypyType: flat: bool, named: bool) -> MypyType:
field_lookups = resolve_field_lookups(ctx.args[0], ctx, django_context) field_lookups = resolve_field_lookups(ctx.args[0], django_context)
if field_lookups is None: if field_lookups is None:
return AnyType(TypeOfAny.from_error) return AnyType(TypeOfAny.from_error)
@@ -146,11 +148,10 @@ def extract_proper_type_queryset_values_list(ctx: MethodContext, django_context:
return helpers.reparametrize_instance(ctx.default_return_type, [model_type, row_type]) return helpers.reparametrize_instance(ctx.default_return_type, [model_type, row_type])
def resolve_field_lookups(lookup_exprs: Sequence[Expression], ctx: Union[FunctionContext, MethodContext], def resolve_field_lookups(lookup_exprs: Sequence[Expression], django_context: DjangoContext) -> Optional[List[str]]:
django_context: DjangoContext) -> Optional[List[str]]:
field_lookups = [] field_lookups = []
for field_lookup_expr in lookup_exprs: for field_lookup_expr in lookup_exprs:
field_lookup = helpers.resolve_string_attribute_value(field_lookup_expr, ctx, django_context) field_lookup = helpers.resolve_string_attribute_value(field_lookup_expr, django_context)
if field_lookup is None: if field_lookup is None:
return None return None
field_lookups.append(field_lookup) field_lookups.append(field_lookup)
@@ -170,7 +171,7 @@ def extract_proper_type_queryset_values(ctx: MethodContext, django_context: Djan
if model_cls is None: if model_cls is None:
return ctx.default_return_type return ctx.default_return_type
field_lookups = resolve_field_lookups(ctx.args[0], ctx, django_context) field_lookups = resolve_field_lookups(ctx.args[0], django_context)
if field_lookups is None: if field_lookups is None:
return AnyType(TypeOfAny.from_error) return AnyType(TypeOfAny.from_error)

View File

@@ -2,17 +2,17 @@
# using this constant. # using this constant.
import re import re
IGNORED_MODULES = {'schema', 'gis_tests', 'admin_widgets', 'admin_filters', 'migrations', IGNORED_MODULES = {'schema', 'gis_tests', 'admin_widgets', 'admin_filters',
'sitemaps_tests', 'staticfiles_tests', 'modeladmin', 'model_forms', 'sitemaps_tests', 'staticfiles_tests', 'modeladmin',
'generic_views', 'forms_tests', 'flatpages_tests', 'admin_utils', 'generic_views', 'forms_tests', 'flatpages_tests',
'admin_ordering', 'admin_changelist', 'admin_views', 'redirects_tests', 'admin_ordering', 'admin_changelist', 'admin_views',
'invalid_models_tests', 'i18n', 'migrate_signals', 'model_formsets', 'invalid_models_tests', 'i18n', 'model_formsets',
'template_tests', 'template_backends', 'test_runner', 'admin_scripts', 'template_tests', 'template_backends', 'test_runner', 'admin_scripts',
'sites_tests', 'inline_formsets', 'foreign_object', 'cache', 'test_client', 'test_client_regress'} 'inline_formsets', 'foreign_object', 'cache'}
MOCK_OBJECTS = ['MockRequest', 'MockCompiler', 'modelz', 'call_count', 'call_args_list', MOCK_OBJECTS = ['MockRequest', 'MockCompiler', 'MockModelAdmin', 'modelz', 'call_count', 'call_args_list',
'call_args', 'MockUser', 'Xtemplate', 'DummyRequest', 'DummyUser', 'MinimalUser', 'DummyNode'] 'call_args', 'MockUser', 'Xtemplate', 'DummyRequest', 'DummyUser', 'MinimalUser', 'DummyNode']
EXTERNAL_MODULES = ['psycopg2', 'PIL', 'selenium', 'oracle', 'mysql', 'sqlite3', 'sqlparse', 'tblib', 'numpy', EXTERNAL_MODULES = ['psycopg2', 'PIL', 'selenium', 'oracle', 'mysql', 'sqlparse', 'tblib', 'numpy',
'bcrypt', 'argon2', 'xml.dom'] 'bcrypt', 'argon2', 'xml.dom']
IGNORED_ERRORS = { IGNORED_ERRORS = {
'__common__': [ '__common__': [
@@ -31,7 +31,6 @@ IGNORED_ERRORS = {
re.compile(r'has no attribute ("|\')_[a-zA-Z_]+("|\')'), re.compile(r'has no attribute ("|\')_[a-zA-Z_]+("|\')'),
"'Settings' object has no attribute", "'Settings' object has no attribute",
'**Dict', '**Dict',
re.compile(r"Expression of type '.*' is not supported"),
'has incompatible type "object"', 'has incompatible type "object"',
'undefined in superclass', 'undefined in superclass',
'Argument after ** must be a mapping', 'Argument after ** must be a mapping',
@@ -65,6 +64,15 @@ IGNORED_ERRORS = {
'Incompatible types in string interpolation', 'Incompatible types in string interpolation',
'"None" has no attribute', '"None" has no attribute',
'has no attribute "assert', 'has no attribute "assert',
'Unsupported dynamic base class',
'error: "HttpResponse" has no attribute "streaming_content"',
'error: "HttpResponse" has no attribute "context_data"',
],
'admin_inlines': [
'error: "HttpResponse" has no attribute "rendered_content"',
],
'admin_utils': [
'"Article" has no attribute "non_field"',
], ],
'aggregation': [ 'aggregation': [
re.compile(r'got "Optional\[(Author|Publisher)\]", expected "Union\[(Author|Publisher), Combinable\]"'), re.compile(r'got "Optional\[(Author|Publisher)\]", expected "Union\[(Author|Publisher), Combinable\]"'),
@@ -101,6 +109,7 @@ IGNORED_ERRORS = {
], ],
'builtin_server': [ 'builtin_server': [
'"ServerHandler" has no attribute', '"ServerHandler" has no attribute',
'Incompatible types in assignment (expression has type "Tuple[BytesIO, BytesIO]"',
], ],
'bulk_create': [ 'bulk_create': [
'has incompatible type "List[Country]"; expected "Iterable[TwoFields]"', 'has incompatible type "List[Country]"; expected "Iterable[TwoFields]"',
@@ -130,7 +139,6 @@ IGNORED_ERRORS = {
'"Employee" has no attribute "id"', '"Employee" has no attribute "id"',
], ],
'custom_managers': [ 'custom_managers': [
'Unsupported dynamic base class',
'Incompatible types in assignment (expression has type "CharField', 'Incompatible types in assignment (expression has type "CharField',
'Item "Book" of "Optional[Book]" has no attribute "favorite_avg"', 'Item "Book" of "Optional[Book]" has no attribute "favorite_avg"',
], ],
@@ -162,7 +170,8 @@ IGNORED_ERRORS = {
'Incompatible types in assignment (expression has type "Optional[Any]", variable has type "FloatModel")' 'Incompatible types in assignment (expression has type "Optional[Any]", variable has type "FloatModel")'
], ],
'decorators': [ 'decorators': [
'"Type[object]" has no attribute "method"' '"Type[object]" has no attribute "method"',
'Value of type variable "_T" of function cannot be "descriptor_wrapper"'
], ],
'expressions_window': [ 'expressions_window': [
'has incompatible type "str"' 'has incompatible type "str"'
@@ -176,7 +185,6 @@ IGNORED_ERRORS = {
], ],
'files': [ 'files': [
'Incompatible types in assignment (expression has type "IOBase", variable has type "File")', 'Incompatible types in assignment (expression has type "IOBase", variable has type "File")',
'Argument 1 to "write" of "SpooledTemporaryFile"',
], ],
'filtered_relation': [ 'filtered_relation': [
'has no attribute "name"', 'has no attribute "name"',
@@ -190,7 +198,7 @@ IGNORED_ERRORS = {
], ],
'from_db_value': [ 'from_db_value': [
'"Cash" has no attribute', '"Cash" has no attribute',
'Argument 1 to "__str__" of "Decimal"', '"__str__" of "Decimal"',
], ],
'get_object_or_404': [ 'get_object_or_404': [
'Argument 1 to "get_object_or_404" has incompatible type "str"; ' 'Argument 1 to "get_object_or_404" has incompatible type "str"; '
@@ -229,13 +237,8 @@ IGNORED_ERRORS = {
], ],
'mail': [ 'mail': [
'List item 1 has incompatible type "None"; expected "str"', 'List item 1 has incompatible type "None"; expected "str"',
'Argument 1 to "push" of "SMTPChannel" has incompatible type "str"; expected "bytes"',
'Value of type "Union[List[Message], str, bytes, None]" is not indexable',
'Incompatible types in assignment ' 'Incompatible types in assignment '
+ '(expression has type "bool", variable has type "Union[SMTP_SSL, SMTP, None]")', + '(expression has type "bool", variable has type "Union[SMTP_SSL, SMTP, None]")',
re.compile(
r'Item "(int|str)" of "Union\[Message, str, int, Any\]" has no attribute "(get_content_type|get_filename)"'
)
], ],
'messages_tests': [ 'messages_tests': [
'List item 0 has incompatible type "Dict[str, Message]"; expected "Message"', 'List item 0 has incompatible type "Dict[str, Message]"; expected "Message"',
@@ -244,9 +247,10 @@ IGNORED_ERRORS = {
], ],
'middleware': [ 'middleware': [
re.compile(r'"(HttpRequest|WSGIRequest)" has no attribute'), re.compile(r'"(HttpRequest|WSGIRequest)" has no attribute'),
'Incompatible types in assignment (expression has type "HttpResponseBase", variable has type "HttpResponse")',
], ],
'many_to_many': [ 'many_to_many': [
'(expression has type "List[Article]", variable has type "RelatedManager[Article]"', '(expression has type "List[Article]", variable has type "Article_RelatedManager2',
'"add" of "RelatedManager" has incompatible type "Article"; expected "Union[Publication, int]"', '"add" of "RelatedManager" has incompatible type "Article"; expected "Union[Publication, int]"',
], ],
'many_to_one': [ 'many_to_one': [
@@ -259,6 +263,20 @@ IGNORED_ERRORS = {
'middleware_exceptions': [ 'middleware_exceptions': [
'Argument 1 to "append" of "list" has incompatible type "Tuple[Any, Any]"; expected "str"' 'Argument 1 to "append" of "list" has incompatible type "Tuple[Any, Any]"; expected "str"'
], ],
'migrate_signals': [
'Value of type "Optional[Any]" is not indexable',
'Argument 1 to "set" has incompatible type "Optional[Any]"; expected "Iterable[Any]"',
],
'migrations': [
'FakeMigration',
'FakeLoader',
'"Manager[Any]" has no attribute "args"',
'Dict entry 0 has incompatible type "Any"',
'Argument 1 to "append" of "list" has incompatible type',
'base class "Model" defined the type as "BaseManager[Any]"',
'Argument 1 to "RunPython" has incompatible type "str"',
],
'model_fields': [ 'model_fields': [
'Item "Field[Any, Any]" of "Union[Field[Any, Any], ForeignObjectRel]" has no attribute', 'Item "Field[Any, Any]" of "Union[Field[Any, Any], ForeignObjectRel]" has no attribute',
'Incompatible types in assignment (expression has type "Type[Person', 'Incompatible types in assignment (expression has type "Type[Person',
@@ -266,6 +284,15 @@ IGNORED_ERRORS = {
'"ImageFile" has no attribute "was_opened"', '"ImageFile" has no attribute "was_opened"',
'Incompatible type for "size" of "FloatModel" (got "object", expected "Union[float, int, str, Combinable]")', 'Incompatible type for "size" of "FloatModel" (got "object", expected "Union[float, int, str, Combinable]")',
'Incompatible type for "value" of "IntegerModel" (got "object", expected', 'Incompatible type for "value" of "IntegerModel" (got "object", expected',
'"Child" has no attribute "get_foo_display"',
],
'model_forms': [
'"render" of "Widget"',
"Module 'django.core.validators' has no attribute 'ValidationError'",
'Incompatible types in assignment',
'NewForm',
'"type" has no attribute "base_fields"',
'Argument "instance" to "InvalidModelForm" has incompatible type "Type[Category]"',
], ],
'model_indexes': [ 'model_indexes': [
'Argument "condition" to "Index" has incompatible type "str"; expected "Optional[Q]"' 'Argument "condition" to "Index" has incompatible type "str"; expected "Optional[Q]"'
@@ -296,9 +323,6 @@ IGNORED_ERRORS = {
'model_enums': [ 'model_enums': [
"'bool' is not a valid base class", "'bool' is not a valid base class",
], ],
'multiple_database': [
'Unexpected attribute "extra_arg" for model "Book"'
],
'null_queries': [ 'null_queries': [
"Cannot resolve keyword 'foo' into field" "Cannot resolve keyword 'foo' into field"
], ],
@@ -361,6 +385,9 @@ IGNORED_ERRORS = {
'settings_tests': [ 'settings_tests': [
'Argument 1 to "Settings" has incompatible type "Optional[str]"; expected "str"' 'Argument 1 to "Settings" has incompatible type "Optional[str]"; expected "str"'
], ],
'shortcuts': [
'error: "Context" has no attribute "request"',
],
'signals': [ 'signals': [
'Argument 1 to "append" of "list" has incompatible type "Tuple[Any, Any, Optional[Any], Any]";' 'Argument 1 to "append" of "list" has incompatible type "Tuple[Any, Any, Optional[Any], Any]";'
], ],
@@ -368,8 +395,11 @@ IGNORED_ERRORS = {
'expression has type "CurrentSiteManager[CustomArticle]", base class "AbstractArticle"', 'expression has type "CurrentSiteManager[CustomArticle]", base class "AbstractArticle"',
"Name 'Optional' is not defined", "Name 'Optional' is not defined",
], ],
'sites_tests': [
'"RequestSite" of "Union[Site, RequestSite]" has no attribute "id"',
],
'syndication_tests': [ 'syndication_tests': [
'List or tuple expected as variable arguments' 'Argument 1 to "add_domain" has incompatible type "*Tuple[object, ...]"',
], ],
'sessions_tests': [ 'sessions_tests': [
'Incompatible types in assignment (expression has type "None", variable has type "int")', 'Incompatible types in assignment (expression has type "None", variable has type "int")',
@@ -404,6 +434,14 @@ IGNORED_ERRORS = {
'"PossessedCar" has no attribute "color"', '"PossessedCar" has no attribute "color"',
'expression has type "None", variable has type "List[str]"', 'expression has type "None", variable has type "List[str]"',
], ],
'test_client': [
'(expression has type "HttpResponse", variable has type "StreamingHttpResponse")'
],
'test_client_regress': [
'(expression has type "Dict[<nothing>, <nothing>]", variable has type "SessionBase")',
'Unsupported left operand type for + ("None")',
'Argument 1 to "len" has incompatible type "Context"; expected "Sized"',
],
'transactions': [ 'transactions': [
'Incompatible types in assignment (expression has type "Thread", variable has type "Callable[[], Any]")' 'Incompatible types in assignment (expression has type "Thread", variable has type "Callable[[], Any]")'
], ],
@@ -432,7 +470,7 @@ IGNORED_ERRORS = {
'No overload variant of "join" matches argument types "str", "None"', 'No overload variant of "join" matches argument types "str", "None"',
'Argument 1 to "Archive" has incompatible type "None"; expected "str"', 'Argument 1 to "Archive" has incompatible type "None"; expected "str"',
'Argument 1 to "to_path" has incompatible type "int"; expected "Union[Path, str]"', 'Argument 1 to "to_path" has incompatible type "int"; expected "Union[Path, str]"',
'Cannot infer type argument 1 of "cached_property"',
], ],
'view_tests': [ 'view_tests': [
"Module 'django.views.debug' has no attribute 'Path'", "Module 'django.views.debug' has no attribute 'Path'",

View File

@@ -5,17 +5,17 @@ import sys
from argparse import ArgumentParser from argparse import ArgumentParser
from collections import defaultdict from collections import defaultdict
from pathlib import Path from pathlib import Path
from typing import Dict, List, Pattern, Union from typing import Dict, List, Pattern, Tuple, Union
from git import Repo from git import RemoteProgress, Repo
from scripts.enabled_test_modules import ( from scripts.enabled_test_modules import (
EXTERNAL_MODULES, IGNORED_ERRORS, IGNORED_MODULES, MOCK_OBJECTS, EXTERNAL_MODULES, IGNORED_ERRORS, IGNORED_MODULES, MOCK_OBJECTS,
) )
DJANGO_COMMIT_REFS = { DJANGO_COMMIT_REFS: Dict[str, Tuple[str, str]] = {
'2.2': 'e8b0903976077b951795938b260211214ed7fe41', '2.2': ('stable/2.2.x', '996be04c3ceb456754d9d527d4d708f30727f07e'),
'3.0': '7ec5962638144cbf4c2e47ea7d8dc02d1ce44394' '3.0': ('stable/3.0.x', 'd9f1792c7649e9f946f4a3a35a76bddf5a412b8b')
} }
PROJECT_DIRECTORY = Path(__file__).parent.parent PROJECT_DIRECTORY = Path(__file__).parent.parent
DJANGO_SOURCE_DIRECTORY = PROJECT_DIRECTORY / 'django-sources' # type: Path DJANGO_SOURCE_DIRECTORY = PROJECT_DIRECTORY / 'django-sources' # type: Path
@@ -75,10 +75,21 @@ def replace_with_clickable_location(error: str, abs_test_folder: Path) -> str:
return error.replace(raw_path, clickable_location) return error.replace(raw_path, clickable_location)
def get_django_repo_object() -> Repo: class ProgressPrinter(RemoteProgress):
def line_dropped(self, line: str) -> None:
print(line)
def update(self, op_code, cur_count, max_count=None, message=''):
print(self._cur_line)
def get_django_repo_object(branch: str) -> Repo:
if not DJANGO_SOURCE_DIRECTORY.exists(): if not DJANGO_SOURCE_DIRECTORY.exists():
DJANGO_SOURCE_DIRECTORY.mkdir(exist_ok=True, parents=False) DJANGO_SOURCE_DIRECTORY.mkdir(exist_ok=True, parents=False)
return Repo.clone_from('https://github.com/django/django.git', DJANGO_SOURCE_DIRECTORY) return Repo.clone_from('https://github.com/django/django.git', DJANGO_SOURCE_DIRECTORY,
progress=ProgressPrinter(),
branch=branch,
depth=100)
else: else:
repo = Repo(DJANGO_SOURCE_DIRECTORY) repo = Repo(DJANGO_SOURCE_DIRECTORY)
return repo return repo
@@ -89,10 +100,13 @@ if __name__ == '__main__':
parser.add_argument('--django_version', choices=['2.2', '3.0'], required=True) parser.add_argument('--django_version', choices=['2.2', '3.0'], required=True)
args = parser.parse_args() args = parser.parse_args()
commit_sha = DJANGO_COMMIT_REFS[args.django_version] # install proper Django version
repo = get_django_repo_object() subprocess.check_call([sys.executable, '-m', 'pip', 'install', f'Django=={args.django_version}.*'])
branch, commit_sha = DJANGO_COMMIT_REFS[args.django_version]
repo = get_django_repo_object(branch)
if repo.head.commit.hexsha != commit_sha: if repo.head.commit.hexsha != commit_sha:
repo.git.fetch('origin') repo.remote('origin').fetch(branch, progress=ProgressPrinter(), depth=100)
repo.git.checkout(commit_sha) repo.git.checkout(commit_sha)
mypy_config_file = (PROJECT_DIRECTORY / 'scripts' / 'mypy.ini').absolute() mypy_config_file = (PROJECT_DIRECTORY / 'scripts' / 'mypy.ini').absolute()

View File

@@ -21,14 +21,14 @@ with open('README.md', 'r') as f:
readme = f.read() readme = f.read()
dependencies = [ dependencies = [
'mypy>=0.750,<0.760', 'mypy>=0.770,<0.780',
'typing-extensions', 'typing-extensions',
'django', 'django',
] ]
setup( setup(
name="django-stubs", name="django-stubs",
version="1.3.0", version="1.5.0",
description='Mypy stubs for Django', description='Mypy stubs for Django',
long_description=readme, long_description=readme,
long_description_content_type='text/markdown', long_description_content_type='text/markdown',

View File

@@ -0,0 +1,43 @@
- case: login_required_bare
main: |
from django.contrib.auth.decorators import login_required
@login_required
def view_func(request): ...
reveal_type(view_func) # N: Revealed type is 'def (request: Any) -> Any'
- case: login_required_fancy
main: |
from django.contrib.auth.decorators import login_required
from django.core.handlers.wsgi import WSGIRequest
from django.http import HttpResponse
@login_required(redirect_field_name='a', login_url='b')
def view_func(request: WSGIRequest, arg: str) -> HttpResponse: ...
reveal_type(view_func) # N: Revealed type is 'def (request: django.core.handlers.wsgi.WSGIRequest, arg: builtins.str) -> django.http.response.HttpResponse'
- case: login_required_weird
main: |
from django.contrib.auth.decorators import login_required
# This is non-conventional usage, but covered in Django tests, so we allow it.
def view_func(request): ...
wrapped_view = login_required(view_func, redirect_field_name='a', login_url='b')
reveal_type(wrapped_view) # N: Revealed type is 'def (request: Any) -> Any'
- case: login_required_incorrect_return
main: |
from django.contrib.auth.decorators import login_required
@login_required() # E: Value of type variable "_VIEW" of function cannot be "Callable[[Any], str]"
def view_func2(request) -> str: ...
- case: user_passes_test
main: |
from django.contrib.auth.decorators import user_passes_test
@user_passes_test(lambda u: u.username.startswith('super'))
def view_func(request): ...
reveal_type(view_func) # N: Revealed type is 'def (request: Any) -> Any'
- case: user_passes_test_bare_is_error
main: |
from django.http.response import HttpResponse
from django.contrib.auth.decorators import user_passes_test
@user_passes_test # E: Argument 1 to "user_passes_test" has incompatible type "Callable[[Any], HttpResponse]"; expected "Callable[[AbstractUser], bool]"
def view_func(request) -> HttpResponse: ...
- case: permission_required
main: |
from django.contrib.auth.decorators import permission_required
@permission_required('polls.can_vote')
def view_func(request): ...

View File

@@ -0,0 +1,28 @@
- case: atomic_bare
main: |
from django.db.transaction import atomic
@atomic
def func(x: int) -> list: ...
reveal_type(func) # N: Revealed type is 'def (x: builtins.int) -> builtins.list[Any]'
- case: atomic_args
main: |
from django.db.transaction import atomic
@atomic(using='bla', savepoint=False)
def func(x: int) -> list: ...
reveal_type(func) # N: Revealed type is 'def (x: builtins.int) -> builtins.list[Any]'
- case: non_atomic_requests_bare
main: |
from django.db.transaction import non_atomic_requests
@non_atomic_requests
def view_func(request): ...
reveal_type(view_func) # N: Revealed type is 'def (request: Any) -> Any'
- case: non_atomic_requests_args
main: |
from django.http.request import HttpRequest
from django.http.response import HttpResponse
from django.db.transaction import non_atomic_requests
@non_atomic_requests
def view_func(request: HttpRequest, arg: str) -> HttpResponse: ...
reveal_type(view_func) # N: Revealed type is 'def (request: django.http.request.HttpRequest, arg: builtins.str) -> django.http.response.HttpResponse'

View File

@@ -648,3 +648,27 @@
abstract = True abstract = True
class User(AbstractUser): class User(AbstractUser):
pass pass
- case: related_manager_is_a_subclass_of_default_manager
main: |
from myapp.models import User
reveal_type(User().orders) # N: Revealed type is 'myapp.models.Order_RelatedManager'
reveal_type(User().orders.get()) # N: Revealed type is 'myapp.models.Order*'
reveal_type(User().orders.manager_method()) # N: Revealed type is 'builtins.int'
installed_apps:
- myapp
files:
- path: myapp/__init__.py
- path: myapp/models.py
content: |
from django.db import models
class User(models.Model):
pass
class OrderManager(models.Manager):
def manager_method(self) -> int:
pass
class Order(models.Model):
objects = OrderManager()
user = models.ForeignKey(to=User, on_delete=models.CASCADE, related_name='orders')

View File

@@ -0,0 +1,181 @@
- case: from_queryset_with_base_manager
main: |
from myapp.models import MyModel
reveal_type(MyModel().objects) # N: Revealed type is 'myapp.models.MyModel_NewManager[myapp.models.MyModel]'
reveal_type(MyModel().objects.get()) # N: Revealed type is 'myapp.models.MyModel*'
reveal_type(MyModel().objects.queryset_method()) # N: Revealed type is 'builtins.str'
installed_apps:
- myapp
files:
- path: myapp/__init__.py
- path: myapp/models.py
content: |
from django.db import models
from django.db.models.manager import BaseManager
class ModelQuerySet(models.QuerySet):
def queryset_method(self) -> str:
return 'hello'
NewManager = BaseManager.from_queryset(ModelQuerySet)
class MyModel(models.Model):
objects = NewManager()
- case: from_queryset_with_manager
main: |
from myapp.models import MyModel
reveal_type(MyModel().objects) # N: Revealed type is 'myapp.models.MyModel_NewManager[myapp.models.MyModel]'
reveal_type(MyModel().objects.get()) # N: Revealed type is 'myapp.models.MyModel*'
reveal_type(MyModel().objects.queryset_method()) # N: Revealed type is 'builtins.str'
installed_apps:
- myapp
files:
- path: myapp/__init__.py
- path: myapp/models.py
content: |
from django.db import models
class ModelQuerySet(models.QuerySet):
def queryset_method(self) -> str:
return 'hello'
NewManager = models.Manager.from_queryset(ModelQuerySet)
class MyModel(models.Model):
objects = NewManager()
- case: from_queryset_returns_intersection_of_manager_and_queryset
main: |
from myapp.models import MyModel, NewManager
reveal_type(NewManager()) # N: Revealed type is 'myapp.models.NewManager'
reveal_type(MyModel.objects) # N: Revealed type is 'myapp.models.MyModel_NewManager[myapp.models.MyModel]'
reveal_type(MyModel.objects.get()) # N: Revealed type is 'Any'
reveal_type(MyModel.objects.manager_only_method()) # N: Revealed type is 'builtins.int'
reveal_type(MyModel.objects.manager_and_queryset_method()) # N: Revealed type is 'builtins.str'
installed_apps:
- myapp
files:
- path: myapp/__init__.py
- path: myapp/models.py
content: |
from django.db import models
class ModelBaseManager(models.Manager):
def manager_only_method(self) -> int:
return 1
class ModelQuerySet(models.QuerySet):
def manager_and_queryset_method(self) -> str:
return 'hello'
NewManager = ModelBaseManager.from_queryset(ModelQuerySet)
class MyModel(models.Model):
objects = NewManager()
- case: from_queryset_with_class_name_provided
main: |
from myapp.models import MyModel, NewManager
reveal_type(NewManager()) # N: Revealed type is 'myapp.models.NewManager'
reveal_type(MyModel.objects) # N: Revealed type is 'myapp.models.MyModel_NewManager[myapp.models.MyModel]'
reveal_type(MyModel.objects.get()) # N: Revealed type is 'Any'
reveal_type(MyModel.objects.manager_only_method()) # N: Revealed type is 'builtins.int'
reveal_type(MyModel.objects.manager_and_queryset_method()) # N: Revealed type is 'builtins.str'
installed_apps:
- myapp
files:
- path: myapp/__init__.py
- path: myapp/models.py
content: |
from django.db import models
class ModelBaseManager(models.Manager):
def manager_only_method(self) -> int:
return 1
class ModelQuerySet(models.QuerySet):
def manager_and_queryset_method(self) -> str:
return 'hello'
NewManager = ModelBaseManager.from_queryset(ModelQuerySet, class_name='NewManager')
class MyModel(models.Model):
objects = NewManager()
- case: from_queryset_with_class_inheritance
main: |
from myapp.models import MyModel
reveal_type(MyModel().objects) # N: Revealed type is 'myapp.models.MyModel_NewManager[myapp.models.MyModel]'
reveal_type(MyModel().objects.get()) # N: Revealed type is 'myapp.models.MyModel*'
reveal_type(MyModel().objects.queryset_method()) # N: Revealed type is 'builtins.str'
installed_apps:
- myapp
files:
- path: myapp/__init__.py
- path: myapp/models.py
content: |
from django.db import models
from django.db.models.manager import BaseManager
class BaseQuerySet(models.QuerySet):
def queryset_method(self) -> str:
return 'hello'
class ModelQuerySet(BaseQuerySet):
pass
NewManager = BaseManager.from_queryset(ModelQuerySet)
class MyModel(models.Model):
objects = NewManager()
- case: from_queryset_with_manager_in_another_directory_and_imports
main: |
from myapp.models import MyModel
reveal_type(MyModel().objects) # N: Revealed type is 'myapp.models.MyModel_NewManager[myapp.models.MyModel]'
reveal_type(MyModel().objects.get()) # N: Revealed type is 'myapp.models.MyModel*'
reveal_type(MyModel().objects.queryset_method) # N: Revealed type is 'def (param: Union[builtins.str, None] =) -> Union[builtins.str, None]'
reveal_type(MyModel().objects.queryset_method('str')) # N: Revealed type is 'Union[builtins.str, None]'
installed_apps:
- myapp
files:
- path: myapp/__init__.py
- path: myapp/models.py
content: |
from django.db import models
from myapp.managers import NewManager
class MyModel(models.Model):
objects = NewManager()
- path: myapp/managers.py
content: |
from typing import Optional
from django.db import models
class ModelQuerySet(models.QuerySet):
def queryset_method(self, param: Optional[str] = None) -> Optional[str]:
return param
NewManager = models.Manager.from_queryset(ModelQuerySet)
- case: from_queryset_with_inherited_manager_and_typing_no_return
disable_cache: true
main: |
from myapp.models import MyModel
reveal_type(MyModel().objects) # N: Revealed type is 'myapp.models.MyModel_NewManager[myapp.models.MyModel]'
reveal_type(MyModel().objects.get()) # N: Revealed type is 'myapp.models.MyModel*'
reveal_type(MyModel().objects.base_queryset_method) # N: Revealed type is 'def (param: Union[builtins.int, builtins.str]) -> <nothing>'
reveal_type(MyModel().objects.base_queryset_method(2)) # N: Revealed type is '<nothing>'
installed_apps:
- myapp
files:
- path: myapp/__init__.py
- path: myapp/models.py
content: |
from django.db import models
from myapp.managers import NewManager
class MyModel(models.Model):
objects = NewManager()
- path: myapp/managers.py
content: |
from django.db import models
from myapp.base_queryset import BaseQuerySet
class ModelQuerySet(BaseQuerySet):
pass
NewManager = models.Manager.from_queryset(ModelQuerySet)
- path: myapp/base_queryset.py
content: |
from typing import NoReturn, Union
from django.db import models
class BaseQuerySet(models.QuerySet):
def base_queryset_method(self, param: Union[int, str]) -> NoReturn:
raise ValueError

View File

@@ -107,3 +107,20 @@
class Blog(models.Model): class Blog(models.Model):
name = models.CharField(max_length=100) name = models.CharField(max_length=100)
publisher = models.ForeignKey(Publisher, on_delete=models.CASCADE) publisher = models.ForeignKey(Publisher, on_delete=models.CASCADE)
- case: values_of_many_to_many_field
main: |
from myapp.models import Author, Book
reveal_type(Book.objects.values('authors')) # N: Revealed type is 'django.db.models.query.ValuesQuerySet[myapp.models.Book, TypedDict({'authors': builtins.int})]'
reveal_type(Author.objects.values('books')) # N: Revealed type is 'django.db.models.query.ValuesQuerySet[myapp.models.Author, TypedDict({'books': builtins.int})]'
installed_apps:
- myapp
files:
- path: myapp/__init__.py
- path: myapp/models.py
content: |
from django.db import models
class Author(models.Model):
pass
class Book(models.Model):
authors = models.ManyToManyField(Author, related_name='books')

View File

@@ -224,3 +224,20 @@
pass pass
class Transaction(models.Model): class Transaction(models.Model):
total = models.IntegerField() total = models.IntegerField()
- case: values_list_of_many_to_many_field
main: |
from myapp.models import Author, Book
reveal_type(Book.objects.values_list('authors')) # N: Revealed type is 'django.db.models.query.ValuesQuerySet[myapp.models.Book, Tuple[builtins.int]]'
reveal_type(Author.objects.values_list('books')) # N: Revealed type is 'django.db.models.query.ValuesQuerySet[myapp.models.Author, Tuple[builtins.int]]'
installed_apps:
- myapp
files:
- path: myapp/__init__.py
- path: myapp/models.py
content: |
from django.db import models
class Author(models.Model):
pass
class Book(models.Model):
authors = models.ManyToManyField(Author, related_name='books')

View File

@@ -48,7 +48,7 @@
class Base(Generic[_T]): class Base(Generic[_T]):
def __init__(self, model_cls: Type[_T]): def __init__(self, model_cls: Type[_T]):
self.model_cls = model_cls self.model_cls = model_cls
reveal_type(self.model_cls._default_manager) # N: Revealed type is 'django.db.models.manager.Manager[django.db.models.base.Model]' reveal_type(self.model_cls._default_manager) # N: Revealed type is 'django.db.models.manager.BaseManager[django.db.models.base.Model]'
class MyModel(models.Model): class MyModel(models.Model):
pass pass
class Child(Base[MyModel]): class Child(Base[MyModel]):
@@ -307,15 +307,15 @@
- case: custom_manager_returns_proper_model_types - case: custom_manager_returns_proper_model_types
main: | main: |
from myapp.models import User from myapp.models import User
reveal_type(User.objects) # N: Revealed type is 'myapp.models.User_MyManager[myapp.models.User]' reveal_type(User.objects) # N: Revealed type is 'myapp.models.User_MyManager2[myapp.models.User]'
reveal_type(User.objects.select_related()) # N: Revealed type is 'myapp.models.User_MyManager[myapp.models.User]' reveal_type(User.objects.select_related()) # N: Revealed type is 'myapp.models.User_MyManager2[myapp.models.User]'
reveal_type(User.objects.get()) # N: Revealed type is 'myapp.models.User*' reveal_type(User.objects.get()) # N: Revealed type is 'myapp.models.User*'
reveal_type(User.objects.get_instance()) # N: Revealed type is 'builtins.int' reveal_type(User.objects.get_instance()) # N: Revealed type is 'builtins.int'
reveal_type(User.objects.get_instance_untyped('hello')) # N: Revealed type is 'Any' reveal_type(User.objects.get_instance_untyped('hello')) # N: Revealed type is 'Any'
from myapp.models import ChildUser from myapp.models import ChildUser
reveal_type(ChildUser.objects) # N: Revealed type is 'myapp.models.ChildUser_MyManager[myapp.models.ChildUser]' reveal_type(ChildUser.objects) # N: Revealed type is 'myapp.models.ChildUser_MyManager2[myapp.models.ChildUser]'
reveal_type(ChildUser.objects.select_related()) # N: Revealed type is 'myapp.models.ChildUser_MyManager[myapp.models.ChildUser]' reveal_type(ChildUser.objects.select_related()) # N: Revealed type is 'myapp.models.ChildUser_MyManager2[myapp.models.ChildUser]'
reveal_type(ChildUser.objects.get()) # N: Revealed type is 'myapp.models.ChildUser*' reveal_type(ChildUser.objects.get()) # N: Revealed type is 'myapp.models.ChildUser*'
reveal_type(ChildUser.objects.get_instance()) # N: Revealed type is 'builtins.int' reveal_type(ChildUser.objects.get_instance()) # N: Revealed type is 'builtins.int'
reveal_type(ChildUser.objects.get_instance_untyped('hello')) # N: Revealed type is 'Any' reveal_type(ChildUser.objects.get_instance_untyped('hello')) # N: Revealed type is 'Any'

View File

@@ -0,0 +1,12 @@
- case: testcase_client_attr
main: |
from django.test.testcases import TestCase
class ExampleTestCase(TestCase):
def test_method(self) -> None:
reveal_type(self.client) # N: Revealed type is 'django.test.client.Client'
resp = self.client.post('/url', {'doit': 'srs'}, 'application/json', False, True, extra='value')
reveal_type(resp.status_code) # N: Revealed type is 'builtins.int'
# Attributes monkey-patched by test Client class:
resp.json()
reveal_type(resp.wsgi_request) # N: Revealed type is 'django.core.handlers.wsgi.WSGIRequest'

View File

@@ -96,6 +96,7 @@
import django.contrib.postgres.aggregates.general import django.contrib.postgres.aggregates.general
import django.contrib.postgres.aggregates.mixins import django.contrib.postgres.aggregates.mixins
import django.contrib.postgres.aggregates.statistics import django.contrib.postgres.aggregates.statistics
import django.contrib.postgres.constraints
import django.contrib.postgres.fields import django.contrib.postgres.fields
import django.contrib.postgres.fields.array import django.contrib.postgres.fields.array
import django.contrib.postgres.fields.citext import django.contrib.postgres.fields.citext
@@ -269,6 +270,7 @@
import django.db.models.constraints import django.db.models.constraints
import django.db.models.deletion import django.db.models.deletion
import django.db.models.expressions import django.db.models.expressions
import django.db.models.enums
import django.db.models.fields import django.db.models.fields
import django.db.models.fields.files import django.db.models.fields.files
import django.db.models.fields.mixins import django.db.models.fields.mixins

View File

@@ -0,0 +1,20 @@
- case: method_decorator_class
main: |
from django.views.generic.base import View
from django.utils.decorators import method_decorator
from django.contrib.auth.decorators import login_required
@method_decorator(login_required, name='dispatch')
class TestView(View): ...
reveal_type(TestView()) # N: Revealed type is 'main.TestView'
- case: method_decorator_function
main: |
from django.views.generic.base import View
from django.utils.decorators import method_decorator
from django.contrib.auth.decorators import login_required
from django.http.response import HttpResponse
from django.http.request import HttpRequest
class TestView(View):
@method_decorator(login_required)
def dispatch(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
return super().dispatch(request, *args, **kwargs)
reveal_type(dispatch) # N: Revealed type is 'def (self: main.TestView, request: django.http.request.HttpRequest, *args: Any, **kwargs: Any) -> django.http.response.HttpResponse'

View File

@@ -0,0 +1,18 @@
- case: cached_property_class_vs_instance_attributes
main: |
from django.utils.functional import cached_property
from typing import List
class Foo:
@cached_property
def attr(self) -> List[str]: ...
reveal_type(attr) # N: Revealed type is 'django.utils.functional.cached_property[builtins.list*[builtins.str]]'
reveal_type(attr.name) # N: Revealed type is 'builtins.str'
reveal_type(Foo.attr) # N: Revealed type is 'django.utils.functional.cached_property[builtins.list*[builtins.str]]'
reveal_type(Foo.attr.func) # N: Revealed type is 'def (*Any, **Any) -> builtins.list*[builtins.str]'
f = Foo()
reveal_type(f.attr) # N: Revealed type is 'builtins.list*[builtins.str]'
f.attr.name # E: "List[str]" has no attribute "name"