mirror of
https://github.com/davidhalter/django-stubs.git
synced 2025-12-13 23:41:55 +08:00
Compare commits
71 Commits
new-readme
...
1.6.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
645ee97e78 | ||
|
|
5bc3759ea2 | ||
|
|
534a028ea2 | ||
|
|
87856754ea | ||
|
|
2f7fac2eaf | ||
|
|
5ff99fd047 | ||
|
|
f77ebcd22c | ||
|
|
34b126e3da | ||
|
|
6e5f5f2cdb | ||
|
|
95252cde60 | ||
|
|
6ef2cf0331 | ||
|
|
9f3b95841b | ||
|
|
e764b1cf4c | ||
|
|
8a64d87917 | ||
|
|
60f3f9dd9f | ||
|
|
ca10ee9242 | ||
|
|
f651f27ddf | ||
|
|
3915aa0639 | ||
|
|
97ec2ee43b | ||
|
|
19c73a106d | ||
|
|
92ef5d9d95 | ||
|
|
f16d1b8cb6 | ||
|
|
c3cdc1c2d5 | ||
|
|
3704d0ab98 | ||
|
|
b1d619edb2 | ||
|
|
e680326c72 | ||
|
|
574a87e68c | ||
|
|
82ae1751ed | ||
|
|
69042783b1 | ||
|
|
391bbc59d5 | ||
|
|
28c76df3b2 | ||
|
|
3d2534ea8d | ||
|
|
54f5f63e71 | ||
|
|
4c5723d368 | ||
|
|
7e0e43135d | ||
|
|
e05b84e32d | ||
|
|
71751d3795 | ||
|
|
25f92e8e56 | ||
|
|
28d47c7e93 | ||
|
|
197cb4058e | ||
|
|
dac2b31fb2 | ||
|
|
8d2600136a | ||
|
|
570772f973 | ||
|
|
d5c1bfb12a | ||
|
|
64cbb0f70e | ||
|
|
6f5a39625e | ||
|
|
bf604a0398 | ||
|
|
92c8dfc93f | ||
|
|
c10c55052c | ||
|
|
96914e466b | ||
|
|
90ed7f332d | ||
|
|
a801501151 | ||
|
|
8ea59985df | ||
|
|
2964ed53d7 | ||
|
|
1b9176f994 | ||
|
|
54d0d018c6 | ||
|
|
1af3a12f2c | ||
|
|
7af89ee6a6 | ||
|
|
afa16bfb74 | ||
|
|
f77073157b | ||
|
|
fe3b95c611 | ||
|
|
d0f9730c53 | ||
|
|
0fdd678d65 | ||
|
|
2397065fa6 | ||
|
|
04023a9f31 | ||
|
|
95e6c94319 | ||
|
|
d96aee7a8b | ||
|
|
2489bb9b04 | ||
|
|
3a8f278c88 | ||
|
|
85b65b4578 | ||
|
|
150e8e862a |
@@ -4,10 +4,19 @@ dist: xenial
|
|||||||
sudo: required
|
sudo: required
|
||||||
jobs:
|
jobs:
|
||||||
include:
|
include:
|
||||||
|
- name: Run plugin test suite with python 3.8
|
||||||
|
python: 3.8
|
||||||
|
script: 'pytest'
|
||||||
|
|
||||||
- name: Run plugin test suite with python 3.7
|
- name: Run plugin test suite with python 3.7
|
||||||
python: 3.7
|
python: 3.7
|
||||||
script: 'pytest'
|
script: 'pytest'
|
||||||
|
|
||||||
|
- name: Typecheck Django 3.0 test suite with python 3.8
|
||||||
|
python: 3.8
|
||||||
|
script: |
|
||||||
|
python ./scripts/typecheck_tests.py --django_version=3.0
|
||||||
|
|
||||||
- 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: |
|
||||||
|
|||||||
111
CONTRIBUTING.md
Normal file
111
CONTRIBUTING.md
Normal 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.
|
||||||
119
README.md
119
README.md
@@ -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
|
||||||
|
|
||||||
[](https://travis-ci.com/typeddjango/django-stubs)
|
[](https://travis-ci.com/typeddjango/django-stubs)
|
||||||
[](http://mypy-lang.org/)
|
[](http://mypy-lang.org/)
|
||||||
[](https://gitter.im/mypy-django/Lobby)
|
[](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,69 +15,112 @@ 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
|
|
||||||
|
|
||||||
| django-stubs | mypy version | django version | python version
|
|
||||||
| ------------ | ---- | ---- | ---- |
|
|
||||||
| 1.3.0 | 0.750 | 2.2.x | ^3.6
|
|
||||||
| 1.2.0 | 0.730 | 2.2.x | ^3.6
|
|
||||||
| 1.1.0 | 0.720 | 2.2.x | ^3.6
|
|
||||||
| 0.12.x | old semantic analyzer (<0.711), dmypy support | 2.1.x | ^3.6
|
|
||||||
|
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
To make mypy aware of the plugin, you need to add
|
To make `mypy` happy, you will need to add:
|
||||||
|
|
||||||
```ini
|
```ini
|
||||||
[mypy]
|
[mypy]
|
||||||
plugins =
|
plugins =
|
||||||
mypy_django_plugin.main
|
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).
|
in your `mypy.ini` or `setup.cfg` [file](https://mypy.readthedocs.io/en/latest/config_file.html).
|
||||||
|
|
||||||
Plugin also requires Django settings module (what you put into `DJANGO_SETTINGS_MODULE` variable) to be specified.
|
Two things happeining here:
|
||||||
|
|
||||||
```ini
|
1. We need to explicitly list our plugin to be loaded by `mypy`
|
||||||
[mypy]
|
2. Our plugin also requires `django` settings module (what you put into `DJANGO_SETTINGS_MODULE` variable) to be specified
|
||||||
strict_optional = True
|
|
||||||
|
|
||||||
# This one is new:
|
|
||||||
[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 `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.
|
|
||||||
|
|
||||||
This fully working [typed boilerplate](https://github.com/wemake-services/wemake-django-template) can serve you as an example.
|
This fully working [typed boilerplate](https://github.com/wemake-services/wemake-django-template) can serve you as an example.
|
||||||
|
|
||||||
|
|
||||||
## Notes
|
## Version compatibility
|
||||||
|
|
||||||
Type implementation monkey-patches Django to add `__class_getitem__` to the `Manager` class.
|
We rely on different `django` and `mypy` versions:
|
||||||
If you would use Python3.7 and do that too in your code, you can make things like
|
|
||||||
|
|
||||||
|
| django-stubs | mypy version | django version | python version
|
||||||
|
| ------------ | ---- | ---- | ---- |
|
||||||
|
| 1.6.0 | 0.780 | 2.2.x \|\| 3.x | ^3.6
|
||||||
|
| 1.5.0 | 0.770 | 2.2.x \|\| 3.x | ^3.6
|
||||||
|
| 1.4.0 | 0.760 | 2.2.x \|\| 3.x | ^3.6
|
||||||
|
| 1.3.0 | 0.750 | 2.2.x \|\| 3.x | ^3.6
|
||||||
|
| 1.2.0 | 0.730 | 2.2.x | ^3.6
|
||||||
|
| 1.1.0 | 0.720 | 2.2.x | ^3.6
|
||||||
|
| 0.12.x | old semantic analyzer (<0.711), dmypy support | 2.1.x | ^3.6
|
||||||
|
|
||||||
|
|
||||||
|
## FAQ
|
||||||
|
|
||||||
|
### Is this an official Django project?
|
||||||
|
|
||||||
|
No, it is not. We are indendepent from Django at the moment.
|
||||||
|
There's a [proposal](https://github.com/django/deps/pull/65) to merge our project into the Django itself.
|
||||||
|
You show your support by linking the PR.
|
||||||
|
|
||||||
|
### Is it safe to use this in production?
|
||||||
|
|
||||||
|
Yes, it is! This project does not affect your runtime at all.
|
||||||
|
It only affects `mypy` type checking process.
|
||||||
|
|
||||||
|
But, it does not make any sense to use this project without `mypy`.
|
||||||
|
|
||||||
|
### mypy crashes when I run it with this plugin installed
|
||||||
|
|
||||||
|
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()`.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
### 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]`.
|
||||||
|
|
||||||
|
This happens because Django classes do not support [`__class_getitem__`](https://www.python.org/dev/peps/pep-0560/#class-getitem) magic method.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
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 create a HttpRequest that's guaranteed to have an authenticated user?
|
||||||
|
|
||||||
|
Django's built in `HttpRequest` has the attribute `user` that resolves to the type
|
||||||
```python
|
```python
|
||||||
class MyUserManager(models.Manager['MyUser']):
|
Union[User, AnonymousUser]
|
||||||
pass
|
```
|
||||||
|
where `User` is the user model specified by the `AUTH_USER_MODEL` setting.
|
||||||
|
|
||||||
class MyUser(models.Model):
|
If you want a `HttpRequest` that you can type-annotate with where you know that the user is authenticated you can subclass the normal `HttpRequest` class like so:
|
||||||
objects = MyUserManager()
|
```python
|
||||||
|
from django.http import HttpRequest
|
||||||
|
from my_user_app.models import MyUser
|
||||||
|
|
||||||
|
class AuthenticatedHttpRequest(HttpRequest):
|
||||||
|
user: MyUser
|
||||||
```
|
```
|
||||||
|
|
||||||
work, which should make a error messages a bit better.
|
And then use `AuthenticatedHttpRequest` instead of the standard `HttpRequest` for when you know that the user is authenticated. For example in views using the `@login_required` decorator.
|
||||||
|
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
black
|
black
|
||||||
pytest-mypy-plugins==1.2.0
|
pytest-mypy-plugins==1.3.0
|
||||||
psycopg2
|
psycopg2-binary
|
||||||
flake8==3.7.9
|
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 .
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
from typing import Any, Callable, Dict, Iterator, List, Optional, Tuple, Union, Iterable
|
from typing import Any, Callable, Dict, Iterator, List, Optional, Tuple, Union, Iterable
|
||||||
|
|
||||||
from django.contrib.auth.forms import AdminPasswordChangeForm
|
|
||||||
from django.forms.boundfield import BoundField
|
from django.forms.boundfield import BoundField
|
||||||
|
from django.forms.forms import BaseForm
|
||||||
from django.forms.utils import ErrorDict
|
from django.forms.utils import ErrorDict
|
||||||
from django.forms.widgets import Media, Widget
|
from django.forms.widgets import Media, Widget
|
||||||
from django.utils.safestring import SafeText
|
from django.utils.safestring import SafeText
|
||||||
@@ -23,7 +23,7 @@ class AdminForm:
|
|||||||
readonly_fields: Any = ...
|
readonly_fields: Any = ...
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
form: AdminPasswordChangeForm,
|
form: BaseForm,
|
||||||
fieldsets: List[Tuple[None, Dict[str, List[str]]]],
|
fieldsets: List[Tuple[None, Dict[str, List[str]]]],
|
||||||
prepopulated_fields: Dict[Any, Any],
|
prepopulated_fields: Dict[Any, Any],
|
||||||
readonly_fields: Optional[Iterable[Any]] = ...,
|
readonly_fields: Optional[Iterable[Any]] = ...,
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from typing import Any, Callable, Dict, Iterator, List, Optional, Sequence, Set, Tuple, Type, Union
|
from typing import Any, Callable, Dict, Iterator, List, Optional, Sequence, Set, Tuple, Type, Union, Mapping, TypeVar
|
||||||
|
|
||||||
|
from django.forms.forms import BaseForm
|
||||||
|
from django.forms.formsets import BaseFormSet
|
||||||
|
from typing_extensions import Literal, TypedDict
|
||||||
|
|
||||||
from django.contrib.admin.filters import ListFilter
|
from django.contrib.admin.filters import ListFilter
|
||||||
from django.contrib.admin.models import LogEntry
|
from django.contrib.admin.models import LogEntry
|
||||||
@@ -26,8 +30,10 @@ from django.db.models.fields import Field
|
|||||||
|
|
||||||
IS_POPUP_VAR: str
|
IS_POPUP_VAR: str
|
||||||
TO_FIELD_VAR: str
|
TO_FIELD_VAR: str
|
||||||
HORIZONTAL: Any
|
HORIZONTAL: Literal[1] = ...
|
||||||
VERTICAL: Any
|
VERTICAL: Literal[2] = ...
|
||||||
|
|
||||||
|
_Direction = Union[Literal[1], Literal[2]]
|
||||||
|
|
||||||
def get_content_type_for_model(obj: Union[Type[Model], Model]) -> ContentType: ...
|
def get_content_type_for_model(obj: Union[Type[Model], Model]) -> ContentType: ...
|
||||||
def get_ul_class(radio_style: int) -> str: ...
|
def get_ul_class(radio_style: int) -> str: ...
|
||||||
@@ -37,21 +43,35 @@ class IncorrectLookupParameters(Exception): ...
|
|||||||
FORMFIELD_FOR_DBFIELD_DEFAULTS: Any
|
FORMFIELD_FOR_DBFIELD_DEFAULTS: Any
|
||||||
csrf_protect_m: Any
|
csrf_protect_m: Any
|
||||||
|
|
||||||
|
class _OptionalFieldOpts(TypedDict, total=False):
|
||||||
|
classes: Sequence[str]
|
||||||
|
description: str
|
||||||
|
|
||||||
|
class _FieldOpts(_OptionalFieldOpts, total=True):
|
||||||
|
fields: Sequence[Union[str, Sequence[str]]]
|
||||||
|
|
||||||
|
# Workaround for mypy issue, a Sequence type should be preferred here.
|
||||||
|
# https://github.com/python/mypy/issues/8921
|
||||||
|
# _FieldsetSpec = Sequence[Tuple[Optional[str], _FieldOpts]]
|
||||||
|
_T = TypeVar("_T")
|
||||||
|
_ListOrTuple = Union[Tuple[_T, ...], List[_T]]
|
||||||
|
_FieldsetSpec = _ListOrTuple[Tuple[Optional[str], _FieldOpts]]
|
||||||
|
|
||||||
class BaseModelAdmin:
|
class BaseModelAdmin:
|
||||||
autocomplete_fields: Any = ...
|
autocomplete_fields: Sequence[str] = ...
|
||||||
raw_id_fields: Any = ...
|
raw_id_fields: Sequence[str] = ...
|
||||||
fields: Any = ...
|
fields: Sequence[Union[str, Sequence[str]]] = ...
|
||||||
exclude: Any = ...
|
exclude: Sequence[str] = ...
|
||||||
fieldsets: Any = ...
|
fieldsets: _FieldsetSpec = ...
|
||||||
form: Any = ...
|
form: Type[BaseForm] = ...
|
||||||
filter_vertical: Any = ...
|
filter_vertical: Sequence[str] = ...
|
||||||
filter_horizontal: Any = ...
|
filter_horizontal: Sequence[str] = ...
|
||||||
radio_fields: Any = ...
|
radio_fields: Mapping[str, _Direction] = ...
|
||||||
prepopulated_fields: Any = ...
|
prepopulated_fields: Mapping[str, Sequence[str]] = ...
|
||||||
formfield_overrides: Any = ...
|
formfield_overrides: Mapping[Type[Field], Mapping[str, Any]] = ...
|
||||||
readonly_fields: Any = ...
|
readonly_fields: Sequence[Union[str, Callable[[Model], Any]]] = ...
|
||||||
ordering: Any = ...
|
ordering: Sequence[str] = ...
|
||||||
sortable_by: Any = ...
|
sortable_by: Sequence[str] = ...
|
||||||
view_on_site: bool = ...
|
view_on_site: bool = ...
|
||||||
show_full_result_count: bool = ...
|
show_full_result_count: bool = ...
|
||||||
checks_class: Any = ...
|
checks_class: Any = ...
|
||||||
@@ -93,7 +113,7 @@ class BaseModelAdmin:
|
|||||||
def has_module_permission(self, request: HttpRequest) -> bool: ...
|
def has_module_permission(self, request: HttpRequest) -> bool: ...
|
||||||
|
|
||||||
class ModelAdmin(BaseModelAdmin):
|
class ModelAdmin(BaseModelAdmin):
|
||||||
list_display: Sequence[Union[str, Callable]] = ...
|
list_display: Sequence[Union[str, Callable[[Model], Any]]] = ...
|
||||||
list_display_links: Optional[Sequence[Union[str, Callable]]] = ...
|
list_display_links: Optional[Sequence[Union[str, Callable]]] = ...
|
||||||
list_filter: Sequence[Union[str, Type[ListFilter], Tuple[str, Type[ListFilter]]]] = ...
|
list_filter: Sequence[Union[str, Type[ListFilter], Tuple[str, Type[ListFilter]]]] = ...
|
||||||
list_select_related: Union[bool, Sequence[str]] = ...
|
list_select_related: Union[bool, Sequence[str]] = ...
|
||||||
@@ -101,21 +121,21 @@ class ModelAdmin(BaseModelAdmin):
|
|||||||
list_max_show_all: int = ...
|
list_max_show_all: int = ...
|
||||||
list_editable: Sequence[str] = ...
|
list_editable: Sequence[str] = ...
|
||||||
search_fields: Sequence[str] = ...
|
search_fields: Sequence[str] = ...
|
||||||
date_hierarchy: Optional[Any] = ...
|
date_hierarchy: Optional[str] = ...
|
||||||
save_as: bool = ...
|
save_as: bool = ...
|
||||||
save_as_continue: bool = ...
|
save_as_continue: bool = ...
|
||||||
save_on_top: bool = ...
|
save_on_top: bool = ...
|
||||||
paginator: Any = ...
|
paginator: Type = ...
|
||||||
preserve_filters: bool = ...
|
preserve_filters: bool = ...
|
||||||
inlines: Sequence[Type[InlineModelAdmin]] = ...
|
inlines: Sequence[Type[InlineModelAdmin]] = ...
|
||||||
add_form_template: Any = ...
|
add_form_template: str = ...
|
||||||
change_form_template: Any = ...
|
change_form_template: str = ...
|
||||||
change_list_template: Any = ...
|
change_list_template: str = ...
|
||||||
delete_confirmation_template: Any = ...
|
delete_confirmation_template: str = ...
|
||||||
delete_selected_confirmation_template: Any = ...
|
delete_selected_confirmation_template: str = ...
|
||||||
object_history_template: Any = ...
|
object_history_template: str = ...
|
||||||
popup_response_template: Any = ...
|
popup_response_template: str = ...
|
||||||
actions: Any = ...
|
actions: Sequence[Union[Callable[[ModelAdmin, HttpRequest, QuerySet], None], str]] = ...
|
||||||
action_form: Any = ...
|
action_form: Any = ...
|
||||||
actions_on_top: bool = ...
|
actions_on_top: bool = ...
|
||||||
actions_on_bottom: bool = ...
|
actions_on_bottom: bool = ...
|
||||||
@@ -227,9 +247,9 @@ class ModelAdmin(BaseModelAdmin):
|
|||||||
def history_view(self, request: HttpRequest, object_id: str, extra_context: None = ...) -> HttpResponse: ...
|
def history_view(self, request: HttpRequest, object_id: str, extra_context: None = ...) -> HttpResponse: ...
|
||||||
|
|
||||||
class InlineModelAdmin(BaseModelAdmin):
|
class InlineModelAdmin(BaseModelAdmin):
|
||||||
model: Any = ...
|
model: Type[Model] = ...
|
||||||
fk_name: Any = ...
|
fk_name: str = ...
|
||||||
formset: Any = ...
|
formset: BaseFormSet = ...
|
||||||
extra: int = ...
|
extra: int = ...
|
||||||
min_num: Optional[int] = ...
|
min_num: Optional[int] = ...
|
||||||
max_num: Optional[int] = ...
|
max_num: Optional[int] = ...
|
||||||
@@ -238,8 +258,8 @@ class InlineModelAdmin(BaseModelAdmin):
|
|||||||
verbose_name_plural: Optional[str] = ...
|
verbose_name_plural: Optional[str] = ...
|
||||||
can_delete: bool = ...
|
can_delete: bool = ...
|
||||||
show_change_link: bool = ...
|
show_change_link: bool = ...
|
||||||
classes: Any = ...
|
classes: Optional[Sequence[str]] = ...
|
||||||
admin_site: Any = ...
|
admin_site: AdminSite = ...
|
||||||
parent_model: Any = ...
|
parent_model: Any = ...
|
||||||
opts: Any = ...
|
opts: Any = ...
|
||||||
has_registered_model: Any = ...
|
has_registered_model: Any = ...
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ class AdminSite:
|
|||||||
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: Optional[Dict[str, Any]] = ...) -> TemplateResponse: ...
|
def logout(self, request: WSGIRequest, extra_context: Optional[Dict[str, Any]] = ...) -> TemplateResponse: ...
|
||||||
def login(self, request: WSGIRequest, extra_context: Optional[Dict[str, Any]] = ...) -> HttpResponse: ...
|
def login(self, request: WSGIRequest, extra_context: Optional[Dict[str, Any]] = ...) -> HttpResponse: ...
|
||||||
|
def _build_app_dict(self, request: WSGIRequest, label: Optional[str] = ...) -> Dict[str, Any]: ...
|
||||||
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, Any]] = ...) -> TemplateResponse: ...
|
def index(self, request: WSGIRequest, extra_context: Optional[Dict[str, Any]] = ...) -> TemplateResponse: ...
|
||||||
def app_index(
|
def app_index(
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
from typing import Callable, TypeVar, overload
|
from typing import Callable, Optional, TypeVar, overload
|
||||||
|
|
||||||
_C = TypeVar("_C", bound=Callable)
|
_C = TypeVar("_C", bound=Callable)
|
||||||
@overload
|
@overload
|
||||||
def staff_member_required(view_func: _C = ..., redirect_field_name: str = ..., login_url: str = ...) -> _C: ...
|
def staff_member_required(
|
||||||
|
view_func: _C = ..., redirect_field_name: Optional[str] = ..., login_url: str = ...
|
||||||
|
) -> _C: ...
|
||||||
@overload
|
@overload
|
||||||
def staff_member_required(view_func: None = ..., redirect_field_name: str = ..., login_url: str = ...) -> Callable: ...
|
def staff_member_required(
|
||||||
|
view_func: None = ..., redirect_field_name: Optional[str] = ..., login_url: str = ...
|
||||||
|
) -> Callable: ...
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ from typing import Any, List, Optional, Type, Union
|
|||||||
|
|
||||||
from django.contrib.auth.backends import ModelBackend
|
from django.contrib.auth.backends import ModelBackend
|
||||||
from django.contrib.auth.base_user import AbstractBaseUser
|
from django.contrib.auth.base_user import AbstractBaseUser
|
||||||
from django.contrib.auth.models import AbstractUser, AnonymousUser
|
from django.contrib.auth.models import AnonymousUser
|
||||||
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.options import Options
|
from django.db.models.options import Options
|
||||||
@@ -29,6 +29,6 @@ def logout(request: HttpRequest) -> None: ...
|
|||||||
def get_user_model() -> Type[Model]: ...
|
def get_user_model() -> Type[Model]: ...
|
||||||
def get_user(request: HttpRequest) -> Union[AbstractBaseUser, AnonymousUser]: ...
|
def get_user(request: HttpRequest) -> Union[AbstractBaseUser, AnonymousUser]: ...
|
||||||
def get_permission_codename(action: str, opts: Options) -> str: ...
|
def get_permission_codename(action: str, opts: Options) -> str: ...
|
||||||
def update_session_auth_hash(request: WSGIRequest, user: AbstractUser) -> None: ...
|
def update_session_auth_hash(request: HttpRequest, user: AbstractBaseUser) -> None: ...
|
||||||
|
|
||||||
default_app_config: str
|
default_app_config: str
|
||||||
|
|||||||
@@ -1,9 +1,15 @@
|
|||||||
|
import sys
|
||||||
from typing import Any, Optional, Tuple, List, overload, TypeVar
|
from typing import Any, Optional, Tuple, List, overload, TypeVar
|
||||||
|
|
||||||
from django.db.models.base import Model
|
from django.db.models.base import Model
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
|
if sys.version_info < (3, 8):
|
||||||
|
from typing_extensions import Literal
|
||||||
|
else:
|
||||||
|
from typing import Literal
|
||||||
|
|
||||||
_T = TypeVar("_T", bound=Model)
|
_T = TypeVar("_T", bound=Model)
|
||||||
|
|
||||||
class BaseUserManager(models.Manager[_T]):
|
class BaseUserManager(models.Manager[_T]):
|
||||||
@@ -20,9 +26,9 @@ class AbstractBaseUser(models.Model):
|
|||||||
def get_username(self) -> str: ...
|
def get_username(self) -> str: ...
|
||||||
def natural_key(self) -> Tuple[str]: ...
|
def natural_key(self) -> Tuple[str]: ...
|
||||||
@property
|
@property
|
||||||
def is_anonymous(self) -> bool: ...
|
def is_anonymous(self) -> Literal[False]: ...
|
||||||
@property
|
@property
|
||||||
def is_authenticated(self) -> bool: ...
|
def is_authenticated(self) -> Literal[True]: ...
|
||||||
def set_password(self, raw_password: Optional[str]) -> None: ...
|
def set_password(self, raw_password: Optional[str]) -> None: ...
|
||||||
def check_password(self, raw_password: str) -> bool: ...
|
def check_password(self, raw_password: str) -> bool: ...
|
||||||
def set_unusable_password(self) -> None: ...
|
def set_unusable_password(self) -> None: ...
|
||||||
|
|||||||
@@ -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]: ...
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
from typing import Any, Dict, Iterator, Optional
|
from typing import Any, Dict, Iterator, Optional
|
||||||
|
|
||||||
from django.contrib.auth.base_user import AbstractBaseUser
|
from django.contrib.auth.base_user import AbstractBaseUser
|
||||||
from django.contrib.auth.models import AbstractUser, User
|
from django.contrib.auth.models import User
|
||||||
from django.contrib.auth.tokens import PasswordResetTokenGenerator
|
from django.contrib.auth.tokens import PasswordResetTokenGenerator
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.core.handlers.wsgi import WSGIRequest
|
from django.core.handlers.wsgi import WSGIRequest
|
||||||
@@ -86,6 +86,6 @@ class AdminPasswordChangeForm(forms.Form):
|
|||||||
password1: Any = ...
|
password1: Any = ...
|
||||||
password2: Any = ...
|
password2: Any = ...
|
||||||
user: User = ...
|
user: User = ...
|
||||||
def __init__(self, user: AbstractUser, *args: Any, **kwargs: Any) -> None: ...
|
def __init__(self, user: AbstractBaseUser, *args: Any, **kwargs: Any) -> None: ...
|
||||||
def clean_password2(self) -> str: ...
|
def clean_password2(self) -> str: ...
|
||||||
def save(self, commit: bool = ...) -> AbstractUser: ...
|
def save(self, commit: bool = ...) -> AbstractBaseUser: ...
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import sys
|
||||||
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.backends import ModelBackend
|
||||||
@@ -9,6 +10,11 @@ from django.db.models.manager import EmptyManager
|
|||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
|
if sys.version_info < (3, 8):
|
||||||
|
from typing_extensions import Literal
|
||||||
|
else:
|
||||||
|
from typing import Literal
|
||||||
|
|
||||||
_AnyUser = Union[Model, "AnonymousUser"]
|
_AnyUser = Union[Model, "AnonymousUser"]
|
||||||
|
|
||||||
def update_last_login(sender: Type[AbstractBaseUser], user: AbstractBaseUser, **kwargs: Any) -> None: ...
|
def update_last_login(sender: Type[AbstractBaseUser], user: AbstractBaseUser, **kwargs: Any) -> None: ...
|
||||||
@@ -105,7 +111,7 @@ class AnonymousUser:
|
|||||||
def has_perms(self, perm_list: Collection[str], obj: Optional[_AnyUser] = ...) -> bool: ...
|
def has_perms(self, perm_list: Collection[str], obj: Optional[_AnyUser] = ...) -> bool: ...
|
||||||
def has_module_perms(self, module: str) -> bool: ...
|
def has_module_perms(self, module: str) -> bool: ...
|
||||||
@property
|
@property
|
||||||
def is_anonymous(self) -> bool: ...
|
def is_anonymous(self) -> Literal[True]: ...
|
||||||
@property
|
@property
|
||||||
def is_authenticated(self) -> bool: ...
|
def is_authenticated(self) -> Literal[False]: ...
|
||||||
def get_username(self) -> str: ...
|
def get_username(self) -> str: ...
|
||||||
|
|||||||
@@ -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: ...
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import Any, Dict, List, Optional, Union
|
from typing import Any, Dict, List, Optional, Union, Protocol
|
||||||
|
|
||||||
from django.contrib.sites.models import Site
|
from django.contrib.sites.models import Site
|
||||||
from django.contrib.sites.requests import RequestSite
|
from django.contrib.sites.requests import RequestSite
|
||||||
@@ -13,10 +13,19 @@ class SitemapNotFound(Exception): ...
|
|||||||
|
|
||||||
def ping_google(sitemap_url: Optional[str] = ..., ping_url: str = ...) -> None: ...
|
def ping_google(sitemap_url: Optional[str] = ..., ping_url: str = ...) -> None: ...
|
||||||
|
|
||||||
|
class _SupportsLen(Protocol):
|
||||||
|
def __len__(self) -> int: ...
|
||||||
|
|
||||||
|
class _SupportsCount(Protocol):
|
||||||
|
def count(self) -> int: ...
|
||||||
|
|
||||||
|
class _SupportsOrdered(Protocol):
|
||||||
|
ordered: bool = ...
|
||||||
|
|
||||||
class Sitemap:
|
class Sitemap:
|
||||||
limit: int = ...
|
limit: int = ...
|
||||||
protocol: Optional[str] = ...
|
protocol: Optional[str] = ...
|
||||||
def items(self) -> List[Any]: ...
|
def items(self) -> Union[_SupportsLen, _SupportsCount, _SupportsOrdered]: ...
|
||||||
def location(self, obj: Model) -> str: ...
|
def location(self, obj: Model) -> str: ...
|
||||||
@property
|
@property
|
||||||
def paginator(self) -> Paginator: ...
|
def paginator(self) -> Paginator: ...
|
||||||
|
|||||||
6
django-stubs/core/cache/backends/base.pyi
vendored
6
django-stubs/core/cache/backends/base.pyi
vendored
@@ -19,16 +19,16 @@ class BaseCache:
|
|||||||
def __init__(self, params: Dict[str, Any]) -> None: ...
|
def __init__(self, params: Dict[str, Any]) -> None: ...
|
||||||
def get_backend_timeout(self, timeout: Any = ...) -> Optional[float]: ...
|
def get_backend_timeout(self, timeout: Any = ...) -> Optional[float]: ...
|
||||||
def make_key(self, key: Any, version: Optional[Any] = ...) -> str: ...
|
def make_key(self, key: Any, version: Optional[Any] = ...) -> str: ...
|
||||||
def add(self, key: Any, value: Any, timeout: Any = ..., version: Optional[Any] = ...) -> None: ...
|
def add(self, key: Any, value: Any, timeout: Any = ..., version: Optional[Any] = ...) -> bool: ...
|
||||||
def get(self, key: Any, default: Optional[Any] = ..., version: Optional[Any] = ...) -> Any: ...
|
def get(self, key: Any, default: Optional[Any] = ..., version: Optional[Any] = ...) -> Any: ...
|
||||||
def set(self, key: Any, value: Any, timeout: Any = ..., version: Optional[Any] = ...) -> None: ...
|
def set(self, key: Any, value: Any, timeout: Any = ..., version: Optional[Any] = ...) -> None: ...
|
||||||
def touch(self, key: Any, timeout: Any = ..., version: Optional[Any] = ...) -> None: ...
|
def touch(self, key: Any, timeout: Any = ..., version: Optional[Any] = ...) -> bool: ...
|
||||||
def delete(self, key: Any, version: Optional[Any] = ...) -> None: ...
|
def delete(self, key: Any, version: Optional[Any] = ...) -> None: ...
|
||||||
def get_many(self, keys: List[str], version: Optional[int] = ...) -> Dict[str, Union[int, str]]: ...
|
def get_many(self, keys: List[str], version: Optional[int] = ...) -> Dict[str, Union[int, str]]: ...
|
||||||
def get_or_set(
|
def get_or_set(
|
||||||
self, key: Any, default: Optional[Any], timeout: Any = ..., version: Optional[int] = ...
|
self, key: Any, default: Optional[Any], timeout: Any = ..., version: Optional[int] = ...
|
||||||
) -> Optional[Any]: ...
|
) -> Optional[Any]: ...
|
||||||
def has_key(self, key: Any, version: Optional[Any] = ...): ...
|
def has_key(self, key: Any, version: Optional[Any] = ...) -> bool: ...
|
||||||
def incr(self, key: str, delta: int = ..., version: Optional[int] = ...) -> int: ...
|
def incr(self, key: str, delta: int = ..., version: Optional[int] = ...) -> int: ...
|
||||||
def decr(self, key: str, delta: int = ..., version: Optional[int] = ...) -> int: ...
|
def decr(self, key: str, delta: int = ..., version: Optional[int] = ...) -> int: ...
|
||||||
def __contains__(self, key: str) -> bool: ...
|
def __contains__(self, key: str) -> bool: ...
|
||||||
|
|||||||
@@ -15,14 +15,14 @@ class SkipFile(UploadFileException): ...
|
|||||||
class StopFutureHandlers(UploadFileException): ...
|
class StopFutureHandlers(UploadFileException): ...
|
||||||
|
|
||||||
class FileUploadHandler:
|
class FileUploadHandler:
|
||||||
chunk_size = ... # type: int
|
chunk_size: int = ...
|
||||||
file_name = ... # type: Optional[str]
|
file_name: Optional[str] = ...
|
||||||
content_type = ... # type: Optional[str]
|
content_type: Optional[str] = ...
|
||||||
content_length = ... # type: Optional[int]
|
content_length: Optional[int] = ...
|
||||||
charset = ... # type: Optional[str]
|
charset: Optional[str] = ...
|
||||||
content_type_extra = ... # type: Optional[Dict[str, str]]
|
content_type_extra: Optional[Dict[str, str]] = ...
|
||||||
request = ... # type: Optional[HttpRequest]
|
request: Optional[HttpRequest] = ...
|
||||||
field_name = ... # type: str
|
field_name: str = ...
|
||||||
def __init__(self, request: Optional[HttpRequest] = ...) -> None: ...
|
def __init__(self, request: Optional[HttpRequest] = ...) -> None: ...
|
||||||
def handle_raw_input(
|
def handle_raw_input(
|
||||||
self,
|
self,
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from typing import Any, Callable, Dict, Optional, Union
|
from typing import Any, Callable, Dict, Optional, Union
|
||||||
|
|
||||||
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.http.response import HttpResponse
|
|
||||||
|
|
||||||
from django.core.handlers import base
|
from django.core.handlers import base
|
||||||
from django.http import HttpRequest
|
from django.http import HttpRequest
|
||||||
|
from django.http.response import HttpResponse
|
||||||
|
|
||||||
_Stream = Union[BytesIO, str]
|
_Stream = Union[BytesIO, str]
|
||||||
_WSGIEnviron = Dict[str, Any]
|
_WSGIEnviron = Dict[str, Any]
|
||||||
@@ -22,7 +20,6 @@ class LimitedStream:
|
|||||||
|
|
||||||
class WSGIRequest(HttpRequest):
|
class WSGIRequest(HttpRequest):
|
||||||
environ: _WSGIEnviron = ...
|
environ: _WSGIEnviron = ...
|
||||||
user: AbstractUser
|
|
||||||
session: SessionBase
|
session: SessionBase
|
||||||
encoding: Any = ...
|
encoding: Any = ...
|
||||||
def __init__(self, environ: _WSGIEnviron) -> None: ...
|
def __init__(self, environ: _WSGIEnviron) -> None: ...
|
||||||
|
|||||||
@@ -22,4 +22,4 @@ class Style:
|
|||||||
|
|
||||||
def make_style(config_string: str = ...) -> Style: ...
|
def make_style(config_string: str = ...) -> Style: ...
|
||||||
def no_style() -> Style: ...
|
def no_style() -> Style: ...
|
||||||
def color_style() -> Style: ...
|
def color_style(force_color: bool = ...) -> Style: ...
|
||||||
|
|||||||
@@ -8,13 +8,13 @@ class InvalidPage(Exception): ...
|
|||||||
class PageNotAnInteger(InvalidPage): ...
|
class PageNotAnInteger(InvalidPage): ...
|
||||||
class EmptyPage(InvalidPage): ...
|
class EmptyPage(InvalidPage): ...
|
||||||
|
|
||||||
class SupportsLen(Protocol):
|
class _SupportsLen(Protocol):
|
||||||
def __len__(self) -> int: ...
|
def __len__(self) -> int: ...
|
||||||
|
|
||||||
class SupportsCount(Protocol):
|
class _SupportsCount(Protocol):
|
||||||
def count(self) -> int: ...
|
def count(self) -> int: ...
|
||||||
|
|
||||||
class SupportsOrdered(Protocol):
|
class _SupportsOrdered(Protocol):
|
||||||
ordered: bool = ...
|
ordered: bool = ...
|
||||||
|
|
||||||
class Paginator:
|
class Paginator:
|
||||||
@@ -24,13 +24,13 @@ class Paginator:
|
|||||||
allow_empty_first_page: bool = ...
|
allow_empty_first_page: bool = ...
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
object_list: Union[SupportsLen, SupportsCount, SupportsOrdered],
|
object_list: Union[_SupportsLen, _SupportsCount, _SupportsOrdered],
|
||||||
per_page: Union[int, str],
|
per_page: Union[int, str],
|
||||||
orphans: int = ...,
|
orphans: int = ...,
|
||||||
allow_empty_first_page: bool = ...,
|
allow_empty_first_page: bool = ...,
|
||||||
) -> None: ...
|
) -> None: ...
|
||||||
def validate_number(self, number: Optional[Union[float, str]]) -> int: ...
|
def validate_number(self, number: Optional[Union[int, float, str]]) -> int: ...
|
||||||
def get_page(self, number: Optional[int]) -> Page: ...
|
def get_page(self, number: Optional[Union[int, float, str]]) -> Page: ...
|
||||||
def page(self, number: Union[int, str]) -> Page: ...
|
def page(self, number: Union[int, str]) -> Page: ...
|
||||||
@property
|
@property
|
||||||
def count(self) -> int: ...
|
def count(self) -> int: ...
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -68,6 +68,9 @@ from .deletion import (
|
|||||||
DO_NOTHING as DO_NOTHING,
|
DO_NOTHING as DO_NOTHING,
|
||||||
PROTECT as PROTECT,
|
PROTECT as PROTECT,
|
||||||
SET as SET,
|
SET as SET,
|
||||||
|
RESTRICT as RESTRICT,
|
||||||
|
ProtectedError as ProtectedError,
|
||||||
|
RestrictedError as RestrictedError,
|
||||||
)
|
)
|
||||||
|
|
||||||
from .query import (
|
from .query import (
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from typing import Any, Callable, Dict, List, Optional, Sequence, Set, Tuple, Type, TypeVar, Union, Collection
|
from typing import Any, Callable, Collection, Dict, Iterable, List, Optional, Set, Tuple, Type, TypeVar, Union
|
||||||
|
|
||||||
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
|
||||||
@@ -7,6 +7,13 @@ from django.db.models.options import Options
|
|||||||
|
|
||||||
_Self = TypeVar("_Self", bound="Model")
|
_Self = TypeVar("_Self", bound="Model")
|
||||||
|
|
||||||
|
class ModelStateFieldsCacheDescriptor: ...
|
||||||
|
|
||||||
|
class ModelState:
|
||||||
|
db: Optional[str] = ...
|
||||||
|
adding: bool = ...
|
||||||
|
fields_cache: ModelStateFieldsCacheDescriptor = ...
|
||||||
|
|
||||||
class ModelBase(type): ...
|
class ModelBase(type): ...
|
||||||
|
|
||||||
class Model(metaclass=ModelBase):
|
class Model(metaclass=ModelBase):
|
||||||
@@ -17,7 +24,12 @@ class Model(metaclass=ModelBase):
|
|||||||
_default_manager: BaseManager[Model]
|
_default_manager: BaseManager[Model]
|
||||||
objects: BaseManager[Any]
|
objects: BaseManager[Any]
|
||||||
pk: Any = ...
|
pk: Any = ...
|
||||||
|
_state: ModelState
|
||||||
def __init__(self: _Self, *args, **kwargs) -> None: ...
|
def __init__(self: _Self, *args, **kwargs) -> None: ...
|
||||||
|
@classmethod
|
||||||
|
def add_to_class(cls, name: str, value: Any): ...
|
||||||
|
@classmethod
|
||||||
|
def from_db(cls, db: Optional[str], field_names: Collection[str], values: Collection[Any]) -> _Self: ...
|
||||||
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]]: ...
|
||||||
def full_clean(self, exclude: Optional[Collection[str]] = ..., validate_unique: bool = ...) -> None: ...
|
def full_clean(self, exclude: Optional[Collection[str]] = ..., validate_unique: bool = ...) -> None: ...
|
||||||
def clean(self) -> None: ...
|
def clean(self) -> None: ...
|
||||||
@@ -31,7 +43,7 @@ class Model(metaclass=ModelBase):
|
|||||||
force_insert: bool = ...,
|
force_insert: bool = ...,
|
||||||
force_update: bool = ...,
|
force_update: bool = ...,
|
||||||
using: Optional[str] = ...,
|
using: Optional[str] = ...,
|
||||||
update_fields: Optional[Union[Sequence[str], str]] = ...,
|
update_fields: Optional[Iterable[str]] = ...,
|
||||||
) -> None: ...
|
) -> None: ...
|
||||||
def save_base(
|
def save_base(
|
||||||
self,
|
self,
|
||||||
@@ -39,17 +51,10 @@ class Model(metaclass=ModelBase):
|
|||||||
force_insert: bool = ...,
|
force_insert: bool = ...,
|
||||||
force_update: bool = ...,
|
force_update: bool = ...,
|
||||||
using: Optional[str] = ...,
|
using: Optional[str] = ...,
|
||||||
update_fields: Optional[Union[Sequence[str], str]] = ...,
|
update_fields: Optional[Iterable[str]] = ...,
|
||||||
): ...
|
): ...
|
||||||
def refresh_from_db(self: _Self, using: Optional[str] = ..., fields: Optional[List[str]] = ...) -> None: ...
|
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]: ...
|
||||||
def __getstate__(self) -> dict: ...
|
def __getstate__(self) -> dict: ...
|
||||||
|
|
||||||
class ModelStateFieldsCacheDescriptor: ...
|
|
||||||
|
|
||||||
class ModelState:
|
|
||||||
db: None = ...
|
|
||||||
adding: bool = ...
|
|
||||||
fields_cache: ModelStateFieldsCacheDescriptor = ...
|
|
||||||
|
|||||||
1
django-stubs/db/models/constants.pyi
Normal file
1
django-stubs/db/models/constants.pyi
Normal file
@@ -0,0 +1 @@
|
|||||||
|
LOOKUP_SEP: str = ...
|
||||||
@@ -11,10 +11,12 @@ def SET_NULL(collector, field, sub_objs, using): ...
|
|||||||
def SET_DEFAULT(collector, field, sub_objs, using): ...
|
def SET_DEFAULT(collector, field, sub_objs, using): ...
|
||||||
def DO_NOTHING(collector, field, sub_objs, using): ...
|
def DO_NOTHING(collector, field, sub_objs, using): ...
|
||||||
def PROTECT(collector, field, sub_objs, using): ...
|
def PROTECT(collector, field, sub_objs, using): ...
|
||||||
|
def RESTRICT(collector, field, sub_objs, using): ...
|
||||||
def SET(value: Any) -> Callable: ...
|
def SET(value: Any) -> Callable: ...
|
||||||
def get_candidate_relations_to_delete(opts: Options) -> Iterable[Field]: ...
|
def get_candidate_relations_to_delete(opts: Options) -> Iterable[Field]: ...
|
||||||
|
|
||||||
class ProtectedError(IntegrityError): ...
|
class ProtectedError(IntegrityError): ...
|
||||||
|
class RestrictedError(IntegrityError): ...
|
||||||
|
|
||||||
class Collector:
|
class Collector:
|
||||||
def __init__(self, using: str) -> None: ...
|
def __init__(self, using: str) -> None: ...
|
||||||
|
|||||||
@@ -10,6 +10,10 @@ class ChoicesMeta(enum.EnumMeta):
|
|||||||
|
|
||||||
class Choices(enum.Enum, metaclass=ChoicesMeta):
|
class Choices(enum.Enum, metaclass=ChoicesMeta):
|
||||||
def __str__(self): ...
|
def __str__(self): ...
|
||||||
|
@property
|
||||||
|
def label(self) -> str: ...
|
||||||
|
@property
|
||||||
|
def value(self) -> Any: ...
|
||||||
|
|
||||||
# fake
|
# fake
|
||||||
class _IntegerChoicesMeta(ChoicesMeta):
|
class _IntegerChoicesMeta(ChoicesMeta):
|
||||||
@@ -18,7 +22,9 @@ class _IntegerChoicesMeta(ChoicesMeta):
|
|||||||
labels: List[str] = ...
|
labels: List[str] = ...
|
||||||
values: List[int] = ...
|
values: List[int] = ...
|
||||||
|
|
||||||
class IntegerChoices(int, Choices, metaclass=_IntegerChoicesMeta): ...
|
class IntegerChoices(int, Choices, metaclass=_IntegerChoicesMeta):
|
||||||
|
@property
|
||||||
|
def value(self) -> int: ...
|
||||||
|
|
||||||
# fake
|
# fake
|
||||||
class _TextChoicesMeta(ChoicesMeta):
|
class _TextChoicesMeta(ChoicesMeta):
|
||||||
@@ -27,4 +33,6 @@ class _TextChoicesMeta(ChoicesMeta):
|
|||||||
labels: List[str] = ...
|
labels: List[str] = ...
|
||||||
values: List[str] = ...
|
values: List[str] = ...
|
||||||
|
|
||||||
class TextChoices(str, Choices, metaclass=_TextChoicesMeta): ...
|
class TextChoices(str, Choices, metaclass=_TextChoicesMeta):
|
||||||
|
@property
|
||||||
|
def value(self) -> str: ...
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
|
from decimal import Decimal
|
||||||
from typing import Any, Callable, Dict, Iterator, List, Optional, Sequence, Set, Tuple, Type, TypeVar, Union, Iterable
|
from typing import Any, Callable, Dict, Iterator, List, Optional, Sequence, Set, Tuple, Type, TypeVar, Union, Iterable
|
||||||
|
|
||||||
from django.db.models.lookups import Lookup
|
from django.db.models.lookups import Lookup
|
||||||
@@ -15,6 +16,8 @@ class SQLiteNumericMixin:
|
|||||||
|
|
||||||
_Self = TypeVar("_Self")
|
_Self = TypeVar("_Self")
|
||||||
|
|
||||||
|
_Numeric = Union[float, Decimal]
|
||||||
|
|
||||||
class Combinable:
|
class Combinable:
|
||||||
ADD: str = ...
|
ADD: str = ...
|
||||||
SUB: str = ...
|
SUB: str = ...
|
||||||
@@ -27,25 +30,25 @@ class Combinable:
|
|||||||
BITLEFTSHIFT: str = ...
|
BITLEFTSHIFT: str = ...
|
||||||
BITRIGHTSHIFT: str = ...
|
BITRIGHTSHIFT: str = ...
|
||||||
def __neg__(self: _Self) -> _Self: ...
|
def __neg__(self: _Self) -> _Self: ...
|
||||||
def __add__(self: _Self, other: Optional[Union[timedelta, Combinable, float, str]]) -> _Self: ...
|
def __add__(self: _Self, other: Optional[Union[timedelta, Combinable, _Numeric, str]]) -> _Self: ...
|
||||||
def __sub__(self: _Self, other: Union[timedelta, Combinable, float]) -> _Self: ...
|
def __sub__(self: _Self, other: Union[timedelta, Combinable, _Numeric]) -> _Self: ...
|
||||||
def __mul__(self: _Self, other: Union[timedelta, Combinable, float]) -> _Self: ...
|
def __mul__(self: _Self, other: Union[timedelta, Combinable, _Numeric]) -> _Self: ...
|
||||||
def __truediv__(self: _Self, other: Union[Combinable, float]) -> _Self: ...
|
def __truediv__(self: _Self, other: Union[Combinable, _Numeric]) -> _Self: ...
|
||||||
def __itruediv__(self: _Self, other: Union[Combinable, float]) -> _Self: ...
|
def __itruediv__(self: _Self, other: Union[Combinable, _Numeric]) -> _Self: ...
|
||||||
def __mod__(self: _Self, other: Union[int, Combinable]) -> _Self: ...
|
def __mod__(self: _Self, other: Union[int, Combinable]) -> _Self: ...
|
||||||
def __pow__(self: _Self, other: Union[float, Combinable]) -> _Self: ...
|
def __pow__(self: _Self, other: Union[_Numeric, Combinable]) -> _Self: ...
|
||||||
def __and__(self: _Self, other: Combinable) -> _Self: ...
|
def __and__(self: _Self, other: Combinable) -> _Self: ...
|
||||||
def bitand(self: _Self, other: int) -> _Self: ...
|
def bitand(self: _Self, other: int) -> _Self: ...
|
||||||
def bitleftshift(self: _Self, other: int) -> _Self: ...
|
def bitleftshift(self: _Self, other: int) -> _Self: ...
|
||||||
def bitrightshift(self: _Self, other: int) -> _Self: ...
|
def bitrightshift(self: _Self, other: int) -> _Self: ...
|
||||||
def __or__(self: _Self, other: Combinable) -> _Self: ...
|
def __or__(self: _Self, other: Combinable) -> _Self: ...
|
||||||
def bitor(self: _Self, other: int) -> _Self: ...
|
def bitor(self: _Self, other: int) -> _Self: ...
|
||||||
def __radd__(self, other: Optional[Union[datetime, float, Combinable]]) -> Combinable: ...
|
def __radd__(self, other: Optional[Union[datetime, _Numeric, Combinable]]) -> Combinable: ...
|
||||||
def __rsub__(self, other: Union[float, Combinable]) -> Combinable: ...
|
def __rsub__(self, other: Union[_Numeric, Combinable]) -> Combinable: ...
|
||||||
def __rmul__(self, other: Union[float, Combinable]) -> Combinable: ...
|
def __rmul__(self, other: Union[_Numeric, Combinable]) -> Combinable: ...
|
||||||
def __rtruediv__(self, other: Union[float, Combinable]) -> Combinable: ...
|
def __rtruediv__(self, other: Union[_Numeric, Combinable]) -> Combinable: ...
|
||||||
def __rmod__(self, other: Union[int, Combinable]) -> Combinable: ...
|
def __rmod__(self, other: Union[int, Combinable]) -> Combinable: ...
|
||||||
def __rpow__(self, other: Union[float, Combinable]) -> Combinable: ...
|
def __rpow__(self, other: Union[_Numeric, Combinable]) -> Combinable: ...
|
||||||
def __rand__(self, other: Any) -> Combinable: ...
|
def __rand__(self, other: Any) -> Combinable: ...
|
||||||
def __ror__(self, other: Any) -> Combinable: ...
|
def __ror__(self, other: Any) -> Combinable: ...
|
||||||
|
|
||||||
|
|||||||
@@ -56,6 +56,10 @@ class Field(RegisterLookupMixin, Generic[_ST, _GT]):
|
|||||||
remote_field: Field
|
remote_field: Field
|
||||||
is_relation: bool
|
is_relation: bool
|
||||||
related_model: Optional[Type[Model]]
|
related_model: Optional[Type[Model]]
|
||||||
|
one_to_many: Optional[bool] = ...
|
||||||
|
one_to_one: Optional[bool] = ...
|
||||||
|
many_to_many: Optional[bool] = ...
|
||||||
|
many_to_one: Optional[bool] = ...
|
||||||
max_length: int
|
max_length: int
|
||||||
model: Type[Model]
|
model: Type[Model]
|
||||||
name: str
|
name: str
|
||||||
@@ -108,7 +112,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: ...
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ class FileField(Field):
|
|||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
upload_to: Union[str, Callable, Path] = ...,
|
upload_to: Union[str, Callable, Path] = ...,
|
||||||
storage: Optional[Storage] = ...,
|
storage: Optional[Union[Storage, Callable[[], Storage]]] = ...,
|
||||||
verbose_name: Optional[Union[str, bytes]] = ...,
|
verbose_name: Optional[Union[str, bytes]] = ...,
|
||||||
name: Optional[str] = ...,
|
name: Optional[str] = ...,
|
||||||
max_length: Optional[int] = ...,
|
max_length: Optional[int] = ...,
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ class _BaseQuerySet(Generic[_T], Sized):
|
|||||||
def union(self: _QS, *other_qs: Any, all: bool = ...) -> _QS: ...
|
def union(self: _QS, *other_qs: Any, all: bool = ...) -> _QS: ...
|
||||||
def intersection(self: _QS, *other_qs: Any) -> _QS: ...
|
def intersection(self: _QS, *other_qs: Any) -> _QS: ...
|
||||||
def difference(self: _QS, *other_qs: Any) -> _QS: ...
|
def difference(self: _QS, *other_qs: Any) -> _QS: ...
|
||||||
def select_for_update(self: _QS, nowait: bool = ..., skip_locked: bool = ..., of: Tuple = ...) -> _QS: ...
|
def select_for_update(self: _QS, nowait: bool = ..., skip_locked: bool = ..., of: Sequence[str] = ...) -> _QS: ...
|
||||||
def select_related(self: _QS, *fields: Any) -> _QS: ...
|
def select_related(self: _QS, *fields: Any) -> _QS: ...
|
||||||
def prefetch_related(self: _QS, *lookups: Any) -> _QS: ...
|
def prefetch_related(self: _QS, *lookups: Any) -> _QS: ...
|
||||||
# TODO: return type
|
# TODO: return type
|
||||||
|
|||||||
@@ -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]: ...
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ class Field:
|
|||||||
initial: Any
|
initial: Any
|
||||||
label: Optional[str]
|
label: Optional[str]
|
||||||
required: bool
|
required: bool
|
||||||
widget: Widget = ...
|
widget: Union[Type[Widget], Widget] = ...
|
||||||
hidden_widget: Any = ...
|
hidden_widget: Any = ...
|
||||||
default_validators: Any = ...
|
default_validators: Any = ...
|
||||||
default_error_messages: Any = ...
|
default_error_messages: Any = ...
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
from typing import Any, Dict, Iterator, List, Mapping, Optional, Sequence, Type, Union
|
from typing import Any, Dict, Iterator, List, Mapping, Optional, Sequence, Type, Union
|
||||||
|
|
||||||
from django.core.exceptions import ValidationError as ValidationError
|
from django.core.exceptions import ValidationError as ValidationError
|
||||||
|
from django.core.files import uploadedfile
|
||||||
from django.forms.boundfield import BoundField
|
from django.forms.boundfield import BoundField
|
||||||
from django.forms.fields import Field
|
from django.forms.fields import Field
|
||||||
from django.forms.renderers import BaseRenderer
|
from django.forms.renderers import BaseRenderer
|
||||||
from django.forms.utils import ErrorDict, ErrorList
|
from django.forms.utils import ErrorDict, ErrorList
|
||||||
from django.forms.widgets import Media, MediaDefiningClass
|
from django.forms.widgets import Media, MediaDefiningClass
|
||||||
|
from django.utils.datastructures import MultiValueDict
|
||||||
from django.utils.safestring import SafeText
|
from django.utils.safestring import SafeText
|
||||||
|
|
||||||
class DeclarativeFieldsMetaclass(MediaDefiningClass): ...
|
class DeclarativeFieldsMetaclass(MediaDefiningClass): ...
|
||||||
@@ -18,11 +20,11 @@ class BaseForm:
|
|||||||
use_required_attribute: bool = ...
|
use_required_attribute: bool = ...
|
||||||
is_bound: bool = ...
|
is_bound: bool = ...
|
||||||
data: Dict[str, Any] = ...
|
data: Dict[str, Any] = ...
|
||||||
files: Optional[Dict[str, Any]] = ...
|
files: MultiValueDict[str, uploadedfile.UploadedFile] = ...
|
||||||
auto_id: str = ...
|
auto_id: Union[bool, str] = ...
|
||||||
initial: Dict[str, Any] = ...
|
initial: Dict[str, Any] = ...
|
||||||
error_class: Type[ErrorList] = ...
|
error_class: Type[ErrorList] = ...
|
||||||
prefix: str = ...
|
prefix: Optional[str] = ...
|
||||||
label_suffix: str = ...
|
label_suffix: str = ...
|
||||||
empty_permitted: bool = ...
|
empty_permitted: bool = ...
|
||||||
fields: Dict[str, Any] = ...
|
fields: Dict[str, Any] = ...
|
||||||
@@ -67,6 +69,14 @@ class BaseForm:
|
|||||||
def hidden_fields(self): ...
|
def hidden_fields(self): ...
|
||||||
def visible_fields(self): ...
|
def visible_fields(self): ...
|
||||||
def get_initial_for_field(self, field: Field, field_name: str) -> Any: ...
|
def get_initial_for_field(self, field: Field, field_name: str) -> Any: ...
|
||||||
|
def _html_output(
|
||||||
|
self,
|
||||||
|
normal_row: str,
|
||||||
|
error_row: str,
|
||||||
|
row_ender: str,
|
||||||
|
help_text_html: str,
|
||||||
|
errors_on_separate_row: bool,
|
||||||
|
) -> SafeText: ...
|
||||||
|
|
||||||
class Form(BaseForm):
|
class Form(BaseForm):
|
||||||
base_fields: Dict[str, Field]
|
base_fields: Dict[str, Field]
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ from typing import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
from django.contrib.auth.base_user import AbstractBaseUser
|
from django.contrib.auth.base_user import AbstractBaseUser
|
||||||
|
from django.contrib.auth.models import AnonymousUser
|
||||||
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.contrib.sites.models import Site
|
||||||
from django.utils.datastructures import CaseInsensitiveMapping, ImmutableList, MultiValueDict
|
from django.utils.datastructures import CaseInsensitiveMapping, ImmutableList, MultiValueDict
|
||||||
@@ -51,7 +52,7 @@ class HttpRequest(BytesIO):
|
|||||||
resolver_match: ResolverMatch = ...
|
resolver_match: ResolverMatch = ...
|
||||||
content_type: Optional[str] = ...
|
content_type: Optional[str] = ...
|
||||||
content_params: Optional[Dict[str, str]] = ...
|
content_params: Optional[Dict[str, str]] = ...
|
||||||
user: AbstractBaseUser
|
user: Union[AbstractBaseUser, AnonymousUser]
|
||||||
site: Site
|
site: Site
|
||||||
session: SessionBase
|
session: SessionBase
|
||||||
encoding: Optional[str] = ...
|
encoding: Optional[str] = ...
|
||||||
|
|||||||
@@ -62,31 +62,34 @@ class HttpResponseBase(Iterable[Any]):
|
|||||||
def __iter__(self) -> Iterator[Any]: ...
|
def __iter__(self) -> Iterator[Any]: ...
|
||||||
|
|
||||||
class HttpResponse(HttpResponseBase):
|
class HttpResponse(HttpResponseBase):
|
||||||
client: Client
|
|
||||||
context: Context
|
|
||||||
content: Any
|
content: Any
|
||||||
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 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: ...
|
||||||
|
def getvalue(self) -> bytes: ...
|
||||||
|
|
||||||
class StreamingHttpResponse(HttpResponseBase):
|
class StreamingHttpResponse(HttpResponseBase):
|
||||||
content: Any
|
content: Any
|
||||||
streaming_content: Iterator[Any]
|
streaming_content: Iterator[Any]
|
||||||
def __init__(self, streaming_content: Iterable[Any] = ..., *args: Any, **kwargs: Any) -> None: ...
|
def __init__(self, streaming_content: Iterable[Any] = ..., *args: Any, **kwargs: Any) -> None: ...
|
||||||
def getvalue(self) -> Any: ...
|
def getvalue(self) -> bytes: ...
|
||||||
|
|
||||||
class FileResponse(StreamingHttpResponse):
|
class FileResponse(StreamingHttpResponse):
|
||||||
client: Client
|
client: Client
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
from typing import Any, Callable, Dict, List, Optional, Protocol, Sequence, Type, TypeVar, Union
|
import sys
|
||||||
|
from typing import Any, Callable, List, Mapping, Optional, overload, Protocol, Sequence, Type, TypeVar, Union
|
||||||
|
|
||||||
from django.db.models.base import Model
|
from django.db.models.base import Model
|
||||||
from django.http.response import (
|
from django.http.response import (
|
||||||
@@ -10,9 +11,14 @@ from django.http.response import (
|
|||||||
from django.db.models import Manager, QuerySet
|
from django.db.models import Manager, QuerySet
|
||||||
from django.http import HttpRequest
|
from django.http import HttpRequest
|
||||||
|
|
||||||
|
if sys.version_info < (3, 8):
|
||||||
|
from typing_extensions import Literal
|
||||||
|
else:
|
||||||
|
from typing import Literal
|
||||||
|
|
||||||
def render_to_response(
|
def render_to_response(
|
||||||
template_name: Union[str, Sequence[str]],
|
template_name: Union[str, Sequence[str]],
|
||||||
context: Optional[Dict[str, Any]] = ...,
|
context: Optional[Mapping[str, Any]] = ...,
|
||||||
content_type: Optional[str] = ...,
|
content_type: Optional[str] = ...,
|
||||||
status: Optional[int] = ...,
|
status: Optional[int] = ...,
|
||||||
using: Optional[str] = ...,
|
using: Optional[str] = ...,
|
||||||
@@ -20,7 +26,7 @@ def render_to_response(
|
|||||||
def render(
|
def render(
|
||||||
request: HttpRequest,
|
request: HttpRequest,
|
||||||
template_name: Union[str, Sequence[str]],
|
template_name: Union[str, Sequence[str]],
|
||||||
context: Optional[Dict[str, Any]] = ...,
|
context: Optional[Mapping[str, Any]] = ...,
|
||||||
content_type: Optional[str] = ...,
|
content_type: Optional[str] = ...,
|
||||||
status: Optional[int] = ...,
|
status: Optional[int] = ...,
|
||||||
using: Optional[str] = ...,
|
using: Optional[str] = ...,
|
||||||
@@ -28,6 +34,15 @@ def render(
|
|||||||
|
|
||||||
class SupportsGetAbsoluteUrl(Protocol): ...
|
class SupportsGetAbsoluteUrl(Protocol): ...
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def redirect(
|
||||||
|
to: Union[Callable, str, SupportsGetAbsoluteUrl], *args: Any, permanent: Literal[True], **kwargs: Any
|
||||||
|
) -> HttpResponsePermanentRedirect: ...
|
||||||
|
@overload
|
||||||
|
def redirect(
|
||||||
|
to: Union[Callable, str, SupportsGetAbsoluteUrl], *args: Any, permanent: Literal[False], **kwargs: Any
|
||||||
|
) -> HttpResponseRedirect: ...
|
||||||
|
@overload
|
||||||
def redirect(
|
def redirect(
|
||||||
to: Union[Callable, str, SupportsGetAbsoluteUrl], *args: Any, permanent: bool = ..., **kwargs: Any
|
to: Union[Callable, str, SupportsGetAbsoluteUrl], *args: Any, permanent: bool = ..., **kwargs: Any
|
||||||
) -> Union[HttpResponseRedirect, HttpResponsePermanentRedirect]: ...
|
) -> Union[HttpResponseRedirect, HttpResponsePermanentRedirect]: ...
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ class Parser:
|
|||||||
builtins: Optional[List[Library]] = ...,
|
builtins: Optional[List[Library]] = ...,
|
||||||
origin: Optional[Origin] = ...,
|
origin: Optional[Origin] = ...,
|
||||||
) -> None: ...
|
) -> None: ...
|
||||||
def parse(self, parse_until: Optional[Tuple[str]] = ...) -> NodeList: ...
|
def parse(self, parse_until: Optional[Tuple[str, ...]] = ...) -> NodeList: ...
|
||||||
def skip_past(self, endtag: str) -> None: ...
|
def skip_past(self, endtag: str) -> None: ...
|
||||||
def extend_nodelist(self, nodelist: NodeList, node: Node, token: Token) -> None: ...
|
def extend_nodelist(self, nodelist: NodeList, node: Node, token: Token) -> None: ...
|
||||||
def error(self, token: Token, e: Union[Exception, str]) -> Exception: ...
|
def error(self, token: Token, e: Union[Exception, str]) -> Exception: ...
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from typing import Any, List, Optional, Dict
|
from typing import Any, List, Iterable, Optional, Dict
|
||||||
|
|
||||||
from django.template.base import Origin, Template
|
from django.template.base import Origin, Template
|
||||||
from django.template.engine import Engine
|
from django.template.engine import Engine
|
||||||
@@ -8,5 +8,5 @@ class Loader:
|
|||||||
get_template_cache: Dict[str, Any] = ...
|
get_template_cache: Dict[str, Any] = ...
|
||||||
def __init__(self, engine: Engine) -> None: ...
|
def __init__(self, engine: Engine) -> None: ...
|
||||||
def get_template(self, template_name: str, skip: Optional[List[Origin]] = ...) -> Template: ...
|
def get_template(self, template_name: str, skip: Optional[List[Origin]] = ...) -> Template: ...
|
||||||
def get_template_sources(self, template_name: str) -> None: ...
|
def get_template_sources(self, template_name: str) -> Iterable[Origin]: ...
|
||||||
def reset(self) -> None: ...
|
def reset(self) -> None: ...
|
||||||
|
|||||||
@@ -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.base_user import AbstractBaseUser
|
||||||
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,44 +78,55 @@ 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: ...
|
||||||
def login(self, **credentials: Any) -> bool: ...
|
def login(self, **credentials: Any) -> bool: ...
|
||||||
def force_login(self, user: AbstractUser, backend: Optional[str] = ...) -> None: ...
|
def force_login(self, user: AbstractBaseUser, backend: Optional[str] = ...) -> None: ...
|
||||||
def logout(self) -> None: ...
|
def logout(self) -> None: ...
|
||||||
|
|
||||||
def conditional_content_removal(request: HttpRequest, response: HttpResponseBase) -> HttpResponse: ...
|
def conditional_content_removal(request: HttpRequest, response: HttpResponseBase) -> HttpResponse: ...
|
||||||
|
|||||||
@@ -126,9 +126,17 @@ class SimpleTestCase(unittest.TestCase):
|
|||||||
self, needle: str, haystack: SafeText, count: Optional[int] = ..., msg_prefix: str = ...
|
self, needle: str, haystack: SafeText, count: Optional[int] = ..., msg_prefix: str = ...
|
||||||
) -> None: ...
|
) -> None: ...
|
||||||
def assertJSONEqual(
|
def assertJSONEqual(
|
||||||
self, raw: str, expected_data: Union[Dict[str, str], bool, str], msg: Optional[str] = ...
|
self,
|
||||||
|
raw: str,
|
||||||
|
expected_data: Union[Dict[str, Any], List[Any], str, int, float, bool, None],
|
||||||
|
msg: Optional[str] = ...,
|
||||||
|
) -> None: ...
|
||||||
|
def assertJSONNotEqual(
|
||||||
|
self,
|
||||||
|
raw: str,
|
||||||
|
expected_data: Union[Dict[str, Any], List[Any], str, int, float, bool, None],
|
||||||
|
msg: Optional[str] = ...,
|
||||||
) -> None: ...
|
) -> None: ...
|
||||||
def assertJSONNotEqual(self, raw: str, expected_data: str, msg: Optional[str] = ...) -> None: ...
|
|
||||||
def assertXMLEqual(self, xml1: str, xml2: str, msg: Optional[str] = ...) -> None: ...
|
def assertXMLEqual(self, xml1: str, xml2: str, msg: Optional[str] = ...) -> None: ...
|
||||||
def assertXMLNotEqual(self, xml1: str, xml2: str, msg: Optional[str] = ...) -> None: ...
|
def assertXMLNotEqual(self, xml1: str, xml2: str, msg: Optional[str] = ...) -> None: ...
|
||||||
|
|
||||||
|
|||||||
@@ -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] = ...
|
||||||
|
|||||||
@@ -1,8 +1,31 @@
|
|||||||
from typing import Any, List, Optional, Tuple
|
from typing import Any, List, Optional, Tuple, overload, Callable, Dict, Union
|
||||||
|
|
||||||
from .resolvers import URLResolver
|
from .resolvers import URLResolver, URLPattern
|
||||||
|
from ..conf.urls import IncludedURLConf
|
||||||
|
from ..http.response import HttpResponseBase
|
||||||
|
|
||||||
def include(arg: Any, namespace: Optional[str] = ...) -> Tuple[List[URLResolver], Optional[str], Optional[str]]: ...
|
def include(arg: Any, namespace: Optional[str] = ...) -> Tuple[List[URLResolver], Optional[str], Optional[str]]: ...
|
||||||
|
|
||||||
path: Any
|
# path()
|
||||||
re_path: Any
|
@overload
|
||||||
|
def path(
|
||||||
|
route: str, view: Callable[..., HttpResponseBase], kwargs: Dict[str, Any] = ..., name: str = ...
|
||||||
|
) -> URLPattern: ...
|
||||||
|
@overload
|
||||||
|
def path(route: str, view: IncludedURLConf, kwargs: Dict[str, Any] = ..., name: str = ...) -> URLResolver: ...
|
||||||
|
@overload
|
||||||
|
def path(
|
||||||
|
route: str, view: List[Union[URLResolver, str]], kwargs: Dict[str, Any] = ..., name: str = ...
|
||||||
|
) -> URLResolver: ...
|
||||||
|
|
||||||
|
# re_path()
|
||||||
|
@overload
|
||||||
|
def re_path(
|
||||||
|
route: str, view: Callable[..., HttpResponseBase], kwargs: Dict[str, Any] = ..., name: str = ...
|
||||||
|
) -> URLPattern: ...
|
||||||
|
@overload
|
||||||
|
def re_path(route: str, view: IncludedURLConf, kwargs: Dict[str, Any] = ..., name: str = ...) -> URLResolver: ...
|
||||||
|
@overload
|
||||||
|
def re_path(
|
||||||
|
route: str, view: List[Union[URLResolver, str]], kwargs: Dict[str, Any] = ..., name: str = ...
|
||||||
|
) -> URLResolver: ...
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ class ResolverMatch:
|
|||||||
url_name: Optional[str] = ...,
|
url_name: Optional[str] = ...,
|
||||||
app_names: Optional[List[Optional[str]]] = ...,
|
app_names: Optional[List[Optional[str]]] = ...,
|
||||||
namespaces: Optional[List[Optional[str]]] = ...,
|
namespaces: Optional[List[Optional[str]]] = ...,
|
||||||
|
route: Optional[str] = ...,
|
||||||
) -> None: ...
|
) -> None: ...
|
||||||
def __getitem__(self, index: int) -> Any: ...
|
def __getitem__(self, index: int) -> Any: ...
|
||||||
# for tuple unpacking
|
# for tuple unpacking
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ _V = TypeVar("_V")
|
|||||||
|
|
||||||
class OrderedSet(MutableSet[_K]):
|
class OrderedSet(MutableSet[_K]):
|
||||||
dict: Dict[_K, None] = ...
|
dict: Dict[_K, None] = ...
|
||||||
|
def __init__(self, iterable: Optional[Iterable[_K]] = ...) -> None: ...
|
||||||
def __contains__(self, item: object) -> bool: ...
|
def __contains__(self, item: object) -> bool: ...
|
||||||
def __iter__(self) -> Iterator[_K]: ...
|
def __iter__(self) -> Iterator[_K]: ...
|
||||||
def __len__(self) -> int: ...
|
def __len__(self) -> int: ...
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -56,4 +56,8 @@ class SimpleLazyObject(LazyObject):
|
|||||||
def __copy__(self) -> List[int]: ...
|
def __copy__(self) -> List[int]: ...
|
||||||
def __deepcopy__(self, memo: Dict[Any, Any]) -> List[int]: ...
|
def __deepcopy__(self, memo: Dict[Any, Any]) -> List[int]: ...
|
||||||
|
|
||||||
def partition(predicate: Callable, values: List[Model]) -> Tuple[List[Model], List[Model]]: ...
|
_PartitionMember = TypeVar("_PartitionMember")
|
||||||
|
|
||||||
|
def partition(
|
||||||
|
predicate: Callable, values: List[_PartitionMember]
|
||||||
|
) -> Tuple[List[_PartitionMember], List[_PartitionMember]]: ...
|
||||||
|
|||||||
@@ -13,6 +13,6 @@ NOCOLOR_PALETTE: str
|
|||||||
DARK_PALETTE: str
|
DARK_PALETTE: str
|
||||||
LIGHT_PALETTE: str
|
LIGHT_PALETTE: str
|
||||||
PALETTES: Any
|
PALETTES: Any
|
||||||
DEFAULT_PALETTE = DARK_PALETTE
|
DEFAULT_PALETTE: str = ...
|
||||||
|
|
||||||
def parse_color_setting(config_string: str) -> Optional[Dict[str, Dict[str, Union[Tuple[str], str]]]]: ...
|
def parse_color_setting(config_string: str) -> Optional[Dict[str, Dict[str, Union[Tuple[str], str]]]]: ...
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ class override(ContextDecorator):
|
|||||||
def __enter__(self) -> None: ...
|
def __enter__(self) -> None: ...
|
||||||
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 get_language() -> Optional[str]: ...
|
def get_language() -> str: ...
|
||||||
def get_language_from_path(path: str) -> Optional[str]: ...
|
def get_language_from_path(path: str) -> Optional[str]: ...
|
||||||
def get_language_bidi() -> bool: ...
|
def get_language_bidi() -> bool: ...
|
||||||
def check_for_language(lang_code: Optional[str]) -> bool: ...
|
def check_for_language(lang_code: Optional[str]) -> bool: ...
|
||||||
@@ -66,6 +66,7 @@ def to_locale(language: str) -> str: ...
|
|||||||
def get_language_from_request(request: WSGIRequest, check_path: bool = ...) -> str: ...
|
def get_language_from_request(request: WSGIRequest, check_path: bool = ...) -> str: ...
|
||||||
def templatize(src: str, **kwargs: Any) -> str: ...
|
def templatize(src: str, **kwargs: Any) -> str: ...
|
||||||
def deactivate_all() -> None: ...
|
def deactivate_all() -> None: ...
|
||||||
|
def get_supported_language_variant(lang_code: str, strict: bool = ...) -> str: ...
|
||||||
def get_language_info(lang_code: str) -> Any: ...
|
def get_language_info(lang_code: str) -> Any: ...
|
||||||
|
|
||||||
from . import trans_real as trans_real
|
from . import trans_real as trans_real
|
||||||
|
|||||||
@@ -1,28 +1,35 @@
|
|||||||
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.forms.models import BaseModelForm
|
||||||
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
|
from django.http import HttpRequest, HttpResponse
|
||||||
|
|
||||||
class FormMixin(ContextMixin):
|
class AbstractFormMixin(ContextMixin):
|
||||||
initial: Dict[str, Any] = ...
|
initial: Dict[str, Any] = ...
|
||||||
form_class: Optional[Type[BaseForm]] = ...
|
form_class: Optional[Type[BaseForm]] = ...
|
||||||
success_url: Optional[Union[str, Callable[..., Any]]] = ...
|
success_url: Optional[Union[str, Callable[..., Any]]] = ...
|
||||||
prefix: Optional[str] = ...
|
prefix: Optional[str] = ...
|
||||||
def get_initial(self) -> Dict[str, Any]: ...
|
def get_initial(self) -> Dict[str, Any]: ...
|
||||||
def get_prefix(self) -> Optional[str]: ...
|
def get_prefix(self) -> Optional[str]: ...
|
||||||
def get_form_class(self) -> Type[BaseForm]: ...
|
|
||||||
def get_form(self, form_class: Optional[Type[BaseForm]] = ...) -> BaseForm: ...
|
|
||||||
def get_form_kwargs(self) -> Dict[str, Any]: ...
|
def get_form_kwargs(self) -> Dict[str, Any]: ...
|
||||||
def get_success_url(self) -> str: ...
|
def get_success_url(self) -> str: ...
|
||||||
|
|
||||||
|
class FormMixin(AbstractFormMixin):
|
||||||
|
def get_form_class(self) -> Type[BaseForm]: ...
|
||||||
|
def get_form(self, form_class: Optional[Type[BaseForm]] = ...) -> BaseForm: ...
|
||||||
def form_valid(self, form: BaseForm) -> HttpResponse: ...
|
def form_valid(self, form: BaseForm) -> HttpResponse: ...
|
||||||
def form_invalid(self, form: BaseForm) -> HttpResponse: ...
|
def form_invalid(self, form: BaseForm) -> HttpResponse: ...
|
||||||
|
|
||||||
class ModelFormMixin(FormMixin, SingleObjectMixin):
|
class ModelFormMixin(AbstractFormMixin, SingleObjectMixin):
|
||||||
fields: Optional[Union[Sequence[str], Literal["__all__"]]] = ...
|
fields: Optional[Union[Sequence[str], Literal["__all__"]]] = ...
|
||||||
|
def get_form_class(self) -> Type[BaseModelForm]: ...
|
||||||
|
def get_form(self, form_class: Optional[Type[BaseModelForm]] = ...) -> BaseModelForm: ...
|
||||||
|
def form_valid(self, form: BaseModelForm) -> HttpResponse: ...
|
||||||
|
def form_invalid(self, form: BaseModelForm) -> HttpResponse: ...
|
||||||
|
|
||||||
class ProcessFormView(View):
|
class ProcessFormView(View):
|
||||||
def get(self, request: HttpRequest, *args: str, **kwargs: Any) -> HttpResponse: ...
|
def get(self, request: HttpRequest, *args: str, **kwargs: Any) -> HttpResponse: ...
|
||||||
|
|||||||
@@ -10,3 +10,5 @@ select = F401, Y
|
|||||||
max_line_length = 120
|
max_line_length = 120
|
||||||
per-file-ignores =
|
per-file-ignores =
|
||||||
*__init__.pyi: F401
|
*__init__.pyi: F401
|
||||||
|
base_user.pyi: Y003
|
||||||
|
models.pyi: Y003
|
||||||
|
|||||||
@@ -311,7 +311,11 @@ def add_new_sym_for_info(info: TypeInfo, *, name: str, sym_type: MypyType) -> No
|
|||||||
|
|
||||||
def build_unannotated_method_args(method_node: FuncDef) -> Tuple[List[Argument], MypyType]:
|
def build_unannotated_method_args(method_node: FuncDef) -> Tuple[List[Argument], MypyType]:
|
||||||
prepared_arguments = []
|
prepared_arguments = []
|
||||||
for argument in method_node.arguments[1:]:
|
try:
|
||||||
|
arguments = method_node.arguments[1:]
|
||||||
|
except AttributeError:
|
||||||
|
arguments = []
|
||||||
|
for argument in arguments:
|
||||||
argument.type_annotation = AnyType(TypeOfAny.unannotated)
|
argument.type_annotation = AnyType(TypeOfAny.unannotated)
|
||||||
prepared_arguments.append(argument)
|
prepared_arguments.append(argument)
|
||||||
return_type = AnyType(TypeOfAny.unannotated)
|
return_type = AnyType(TypeOfAny.unannotated)
|
||||||
@@ -343,6 +347,7 @@ def copy_method_to_another_class(ctx: ClassDefContext, self_type: Instance,
|
|||||||
arguments = []
|
arguments = []
|
||||||
bound_return_type = semanal_api.anal_type(method_type.ret_type,
|
bound_return_type = semanal_api.anal_type(method_type.ret_type,
|
||||||
allow_placeholder=True)
|
allow_placeholder=True)
|
||||||
|
|
||||||
assert bound_return_type is not None
|
assert bound_return_type is not None
|
||||||
|
|
||||||
if isinstance(bound_return_type, PlaceholderNode):
|
if isinstance(bound_return_type, PlaceholderNode):
|
||||||
@@ -352,6 +357,10 @@ def copy_method_to_another_class(ctx: ClassDefContext, self_type: Instance,
|
|||||||
method_type.arg_types[1:],
|
method_type.arg_types[1:],
|
||||||
method_node.arguments[1:]):
|
method_node.arguments[1:]):
|
||||||
bound_arg_type = semanal_api.anal_type(arg_type, allow_placeholder=True)
|
bound_arg_type = semanal_api.anal_type(arg_type, allow_placeholder=True)
|
||||||
|
if bound_arg_type is None and not semanal_api.final_iteration:
|
||||||
|
semanal_api.defer()
|
||||||
|
return
|
||||||
|
|
||||||
assert bound_arg_type is not None
|
assert bound_arg_type is not None
|
||||||
|
|
||||||
if isinstance(bound_arg_type, PlaceholderNode):
|
if isinstance(bound_arg_type, PlaceholderNode):
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
import configparser
|
import configparser
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from typing import Callable, Dict, List, Optional, Tuple
|
from typing import Callable, Dict, List, NoReturn, Optional, Tuple, cast
|
||||||
|
|
||||||
from django.db.models.fields.related import RelatedField
|
from django.db.models.fields.related import RelatedField
|
||||||
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 (
|
||||||
@@ -52,25 +51,40 @@ def add_new_manager_base(ctx: ClassDefContext) -> None:
|
|||||||
|
|
||||||
|
|
||||||
def extract_django_settings_module(config_file_path: Optional[str]) -> str:
|
def extract_django_settings_module(config_file_path: Optional[str]) -> str:
|
||||||
errors = Errors()
|
|
||||||
if config_file_path is None:
|
def exit(error_type: int) -> NoReturn:
|
||||||
errors.report(0, None, "'django_settings_module' is not set: no mypy config file specified")
|
"""Using mypy's argument parser, raise `SystemExit` to fail hard if validation fails.
|
||||||
errors.raise_error()
|
|
||||||
|
Considering that the plugin's startup duration is around double as long as mypy's, this aims to
|
||||||
|
import and construct objects only when that's required - which happens once and terminates the
|
||||||
|
run. Considering that most of the runs are successful, there's no need for this to linger in the
|
||||||
|
global scope.
|
||||||
|
"""
|
||||||
|
from mypy.main import CapturableArgumentParser
|
||||||
|
|
||||||
|
usage = """(config)
|
||||||
|
...
|
||||||
|
[mypy.plugins.django_stubs]
|
||||||
|
django_settings_module: str (required)
|
||||||
|
...
|
||||||
|
""".replace("\n" + 8 * " ", "\n")
|
||||||
|
handler = CapturableArgumentParser(prog='(django-stubs) mypy', usage=usage)
|
||||||
|
messages = {1: 'mypy config file is not specified or found',
|
||||||
|
2: 'no section [mypy.plugins.django-stubs]',
|
||||||
|
3: 'the setting is not provided'}
|
||||||
|
handler.error("'django_settings_module' is not set: " + messages[error_type])
|
||||||
|
|
||||||
parser = configparser.ConfigParser()
|
parser = configparser.ConfigParser()
|
||||||
parser.read(config_file_path) # type: ignore
|
try:
|
||||||
|
parser.read_file(open(cast(str, config_file_path), 'r'), source=config_file_path)
|
||||||
|
except (IsADirectoryError, OSError):
|
||||||
|
exit(1)
|
||||||
|
|
||||||
if not parser.has_section('mypy.plugins.django-stubs'):
|
section = 'mypy.plugins.django-stubs'
|
||||||
errors.report(0, None, "'django_settings_module' is not set: no section [mypy.plugins.django-stubs]",
|
if not parser.has_section(section):
|
||||||
file=config_file_path)
|
exit(2)
|
||||||
errors.raise_error()
|
settings = parser.get(section, 'django_settings_module', fallback=None) or exit(3)
|
||||||
if not parser.has_option('mypy.plugins.django-stubs', 'django_settings_module'):
|
return cast(str, settings).strip('\'"')
|
||||||
errors.report(0, None, "'django_settings_module' is not set: setting is not provided",
|
|
||||||
file=config_file_path)
|
|
||||||
errors.raise_error()
|
|
||||||
|
|
||||||
django_settings_module = parser.get('mypy.plugins.django-stubs', 'django_settings_module').strip('\'"')
|
|
||||||
return django_settings_module
|
|
||||||
|
|
||||||
|
|
||||||
class NewSemanalDjangoPlugin(Plugin):
|
class NewSemanalDjangoPlugin(Plugin):
|
||||||
|
|||||||
@@ -1,16 +1,34 @@
|
|||||||
from mypy.plugin import AttributeContext
|
from mypy.plugin import AttributeContext
|
||||||
from mypy.types import Instance
|
from mypy.types import Instance
|
||||||
from mypy.types import Type as MypyType
|
from mypy.types import Type as MypyType
|
||||||
|
from mypy.types import UnionType
|
||||||
|
|
||||||
from mypy_django_plugin.django.context import DjangoContext
|
from mypy_django_plugin.django.context import DjangoContext
|
||||||
from mypy_django_plugin.lib import helpers
|
from mypy_django_plugin.lib import helpers
|
||||||
|
|
||||||
|
|
||||||
def set_auth_user_model_as_type_for_request_user(ctx: AttributeContext, django_context: DjangoContext) -> MypyType:
|
def set_auth_user_model_as_type_for_request_user(ctx: AttributeContext, django_context: DjangoContext) -> MypyType:
|
||||||
auth_user_model = django_context.settings.AUTH_USER_MODEL
|
# Imported here because django isn't properly loaded yet when module is loaded
|
||||||
model_cls = django_context.apps_registry.get_model(auth_user_model)
|
from django.contrib.auth.base_user import AbstractBaseUser
|
||||||
model_info = helpers.lookup_class_typeinfo(helpers.get_typechecker_api(ctx), model_cls)
|
from django.contrib.auth.models import AnonymousUser
|
||||||
if model_info is None:
|
|
||||||
|
abstract_base_user_info = helpers.lookup_class_typeinfo(helpers.get_typechecker_api(ctx), AbstractBaseUser)
|
||||||
|
anonymous_user_info = helpers.lookup_class_typeinfo(helpers.get_typechecker_api(ctx), AnonymousUser)
|
||||||
|
|
||||||
|
# This shouldn't be able to happen, as we managed to import the models above.
|
||||||
|
assert abstract_base_user_info is not None
|
||||||
|
assert anonymous_user_info is not None
|
||||||
|
|
||||||
|
if ctx.default_attr_type != UnionType([Instance(abstract_base_user_info, []), Instance(anonymous_user_info, [])]):
|
||||||
|
# Type has been changed from the default in django-stubs.
|
||||||
|
# I.e. HttpRequest has been subclassed and user-type overridden, so let's leave it as is.
|
||||||
return ctx.default_attr_type
|
return ctx.default_attr_type
|
||||||
|
|
||||||
return Instance(model_info, [])
|
auth_user_model = django_context.settings.AUTH_USER_MODEL
|
||||||
|
user_cls = django_context.apps_registry.get_model(auth_user_model)
|
||||||
|
user_info = helpers.lookup_class_typeinfo(helpers.get_typechecker_api(ctx), user_cls)
|
||||||
|
|
||||||
|
if user_info is None:
|
||||||
|
return ctx.default_attr_type
|
||||||
|
|
||||||
|
return UnionType([Instance(user_info, []), Instance(anonymous_user_info, [])])
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
[pytest]
|
[pytest]
|
||||||
testpaths = ./test-data
|
testpaths =
|
||||||
|
./test-plugin
|
||||||
|
./test-data
|
||||||
addopts =
|
addopts =
|
||||||
--tb=native
|
--tb=native
|
||||||
-s
|
-s
|
||||||
|
|||||||
@@ -64,7 +64,12 @@ 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'
|
'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': [
|
'admin_utils': [
|
||||||
'"Article" has no attribute "non_field"',
|
'"Article" has no attribute "non_field"',
|
||||||
@@ -104,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]"',
|
||||||
@@ -164,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"'
|
||||||
@@ -178,6 +185,8 @@ 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 "TextIOWrapper" has incompatible type "File"; expected "BinaryIO"',
|
||||||
|
'Incompatible types in assignment (expression has type "BinaryIO", variable has type "File")',
|
||||||
],
|
],
|
||||||
'filtered_relation': [
|
'filtered_relation': [
|
||||||
'has no attribute "name"',
|
'has no attribute "name"',
|
||||||
@@ -191,7 +200,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"; '
|
||||||
@@ -240,6 +249,7 @@ 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 "Article_RelatedManager2',
|
'(expression has type "List[Article]", variable has type "Article_RelatedManager2',
|
||||||
@@ -276,6 +286,7 @@ 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': [
|
'model_forms': [
|
||||||
'"render" of "Widget"',
|
'"render" of "Widget"',
|
||||||
@@ -369,6 +380,7 @@ IGNORED_ERRORS = {
|
|||||||
'responses': [
|
'responses': [
|
||||||
'Argument 1 to "TextIOWrapper" has incompatible type "HttpResponse"; expected "IO[bytes]"',
|
'Argument 1 to "TextIOWrapper" has incompatible type "HttpResponse"; expected "IO[bytes]"',
|
||||||
'"FileLike" has no attribute "closed"',
|
'"FileLike" has no attribute "closed"',
|
||||||
|
'Argument 1 to "TextIOWrapper" has incompatible type "HttpResponse"; expected "BinaryIO"',
|
||||||
],
|
],
|
||||||
'reverse_lookup': [
|
'reverse_lookup': [
|
||||||
"Cannot resolve keyword 'choice' into field"
|
"Cannot resolve keyword 'choice' into field"
|
||||||
@@ -376,6 +388,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]";'
|
||||||
],
|
],
|
||||||
@@ -387,7 +402,7 @@ IGNORED_ERRORS = {
|
|||||||
'"RequestSite" of "Union[Site, RequestSite]" has no attribute "id"',
|
'"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")',
|
||||||
@@ -428,6 +443,7 @@ IGNORED_ERRORS = {
|
|||||||
'test_client_regress': [
|
'test_client_regress': [
|
||||||
'(expression has type "Dict[<nothing>, <nothing>]", variable has type "SessionBase")',
|
'(expression has type "Dict[<nothing>, <nothing>]", variable has type "SessionBase")',
|
||||||
'Unsupported left operand type for + ("None")',
|
'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]")'
|
||||||
@@ -435,9 +451,11 @@ IGNORED_ERRORS = {
|
|||||||
'urlpatterns': [
|
'urlpatterns': [
|
||||||
'"object" not callable',
|
'"object" not callable',
|
||||||
'"None" not callable',
|
'"None" not callable',
|
||||||
|
'Argument 2 to "path" has incompatible type "Callable[[Any], None]"',
|
||||||
|
'Incompatible return value type (got "None", expected "HttpResponseBase")',
|
||||||
],
|
],
|
||||||
'urlpatterns_reverse': [
|
'urlpatterns_reverse': [
|
||||||
'List or tuple expected as variable arguments',
|
'No overload variant of "path" matches argument types "str", "None"',
|
||||||
'No overload variant of "zip" matches argument types "Any", "object"',
|
'No overload variant of "zip" matches argument types "Any", "object"',
|
||||||
'Argument 1 to "get_callable" has incompatible type "int"'
|
'Argument 1 to "get_callable" has incompatible type "int"'
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
from pytest_mypy.collect import File
|
from pytest_mypy_plugins.collect import File
|
||||||
from pytest_mypy.item import YamlTestItem
|
from pytest_mypy_plugins.item import YamlTestItem
|
||||||
|
|
||||||
|
|
||||||
def django_plugin_hook(test_item: YamlTestItem) -> None:
|
def django_plugin_hook(test_item: YamlTestItem) -> None:
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ from scripts.enabled_test_modules import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
DJANGO_COMMIT_REFS: Dict[str, Tuple[str, str]] = {
|
DJANGO_COMMIT_REFS: Dict[str, Tuple[str, str]] = {
|
||||||
'2.2': ('stable/2.2.x', '86befcc172c23170a720b3e0c06db51a99b3da59'),
|
'2.2': ('stable/2.2.x', '8093aaa8ff9dd7386a069c6eb49fcc1c5980c033'),
|
||||||
'3.0': ('stable/3.0.x', '6cb30414bc0f83b49afc4cae76d4af5656effe9a')
|
'3.0': ('stable/3.0.x', '44da7abda848f05caaed74f6a749038c87dedfda')
|
||||||
}
|
}
|
||||||
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
|
||||||
|
|||||||
42
setup.py
42
setup.py
@@ -9,7 +9,7 @@ def find_stub_files(name: str) -> List[str]:
|
|||||||
result = []
|
result = []
|
||||||
for root, dirs, files in os.walk(name):
|
for root, dirs, files in os.walk(name):
|
||||||
for file in files:
|
for file in files:
|
||||||
if file.endswith('.pyi'):
|
if file.endswith(".pyi"):
|
||||||
if os.path.sep in root:
|
if os.path.sep in root:
|
||||||
sub_root = root.split(os.path.sep, 1)[-1]
|
sub_root = root.split(os.path.sep, 1)[-1]
|
||||||
file = os.path.join(sub_root, file)
|
file = os.path.join(sub_root, file)
|
||||||
@@ -17,34 +17,42 @@ def find_stub_files(name: str) -> List[str]:
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
with open('README.md', 'r') as f:
|
with open("README.md", "r") as f:
|
||||||
readme = f.read()
|
readme = f.read()
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
'mypy>=0.760,<0.770',
|
"mypy>=0.782,<0.790",
|
||||||
'typing-extensions',
|
"typing-extensions",
|
||||||
'django',
|
"django",
|
||||||
]
|
]
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name="django-stubs",
|
name="django-stubs",
|
||||||
version="1.4.0",
|
version="1.6.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",
|
||||||
license='MIT',
|
license="MIT",
|
||||||
url="https://github.com/typeddjango/django-stubs",
|
url="https://github.com/typeddjango/django-stubs",
|
||||||
author="Maksim Kurnikov",
|
author="Maksim Kurnikov",
|
||||||
author_email="maxim.kurnikov@gmail.com",
|
author_email="maxim.kurnikov@gmail.com",
|
||||||
py_modules=[],
|
py_modules=[],
|
||||||
python_requires='>=3.6',
|
python_requires=">=3.6",
|
||||||
install_requires=dependencies,
|
install_requires=dependencies,
|
||||||
packages=['django-stubs', *find_packages(exclude=['scripts'])],
|
packages=["django-stubs", *find_packages(exclude=["scripts"])],
|
||||||
package_data={'django-stubs': find_stub_files('django-stubs')},
|
package_data={"django-stubs": find_stub_files("django-stubs")},
|
||||||
classifiers=[
|
classifiers=[
|
||||||
'Development Status :: 3 - Alpha',
|
"Development Status :: 3 - Alpha",
|
||||||
'License :: OSI Approved :: MIT License',
|
"License :: OSI Approved :: MIT License",
|
||||||
'Programming Language :: Python :: 3.6',
|
"Programming Language :: Python :: 3.6",
|
||||||
'Programming Language :: Python :: 3.7'
|
"Programming Language :: Python :: 3.7",
|
||||||
]
|
"Programming Language :: Python :: 3.8",
|
||||||
|
"Framework :: Django",
|
||||||
|
"Framework :: Django :: 2.2",
|
||||||
|
"Framework :: Django :: 3.0",
|
||||||
|
"Typing :: Typed",
|
||||||
|
],
|
||||||
|
project_urls={
|
||||||
|
"Release notes": "https://github.com/typeddjango/django-stubs/releases",
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|||||||
130
test-data/typecheck/contrib/admin/test_options.yml
Normal file
130
test-data/typecheck/contrib/admin/test_options.yml
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
# "Happy path" test for model admin, trying to cover as many valid
|
||||||
|
# configurations as possible.
|
||||||
|
- case: test_full_admin
|
||||||
|
main: |
|
||||||
|
from django.contrib import admin
|
||||||
|
from django.forms import Form, Textarea
|
||||||
|
from django.db import models
|
||||||
|
from django.core.paginator import Paginator
|
||||||
|
from django.contrib.admin.sites import AdminSite
|
||||||
|
from django.db.models.options import Options
|
||||||
|
from django.http.request import HttpRequest
|
||||||
|
from django.db.models.query import QuerySet
|
||||||
|
|
||||||
|
def an_action(modeladmin: admin.ModelAdmin, request: HttpRequest, queryset: QuerySet) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
class A(admin.ModelAdmin):
|
||||||
|
# BaseModelAdmin
|
||||||
|
autocomplete_fields = ("strs",)
|
||||||
|
raw_id_fields = ["strs"]
|
||||||
|
fields = (
|
||||||
|
"a field",
|
||||||
|
["a", "list of", "fields"],
|
||||||
|
)
|
||||||
|
exclude = ("a", "b")
|
||||||
|
fieldsets = [
|
||||||
|
(None, {"fields": ["a", "b"]}),
|
||||||
|
("group", {"fields": ("c",), "classes": ("a",), "description": "foo"}),
|
||||||
|
]
|
||||||
|
form = Form
|
||||||
|
filter_vertical = ("fields",)
|
||||||
|
filter_horizontal = ("plenty", "of", "fields")
|
||||||
|
radio_fields = {
|
||||||
|
"some_field": admin.VERTICAL,
|
||||||
|
"another_field": admin.HORIZONTAL,
|
||||||
|
}
|
||||||
|
prepopulated_fields = {"slug": ("title",)}
|
||||||
|
formfield_overrides = {models.TextField: {"widget": Textarea}}
|
||||||
|
readonly_fields = ("date_modified",)
|
||||||
|
ordering = ("-pk", "date_modified")
|
||||||
|
sortable_by = ["pk"]
|
||||||
|
view_on_site = True
|
||||||
|
show_full_result_count = False
|
||||||
|
|
||||||
|
# ModelAdmin
|
||||||
|
list_display = ("pk",)
|
||||||
|
list_display_links = ("str",)
|
||||||
|
list_filter = ("str", admin.SimpleListFilter, ("str", admin.SimpleListFilter))
|
||||||
|
list_select_related = True
|
||||||
|
list_per_page = 1
|
||||||
|
list_max_show_all = 2
|
||||||
|
list_editable = ("a", "b")
|
||||||
|
search_fields = ("c", "d")
|
||||||
|
date_hirearchy = "f"
|
||||||
|
save_as = False
|
||||||
|
save_as_continue = True
|
||||||
|
save_on_top = False
|
||||||
|
paginator = Paginator
|
||||||
|
presserve_filters = False
|
||||||
|
inlines = (admin.TabularInline, admin.StackedInline)
|
||||||
|
add_form_template = "template"
|
||||||
|
change_form_template = "template"
|
||||||
|
change_list_template = "template"
|
||||||
|
delete_confirmation_template = "template"
|
||||||
|
delete_selected_confirmation_template = "template"
|
||||||
|
object_history_template = "template"
|
||||||
|
popup_response_template = "template"
|
||||||
|
actions = (an_action, "a_method_action")
|
||||||
|
actions_on_top = True
|
||||||
|
actions_on_bottom = False
|
||||||
|
actions_selection_counter = True
|
||||||
|
admin_site = AdminSite()
|
||||||
|
|
||||||
|
def a_method_action(self, request, queryset):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# This test is here to make sure we're not running into a mypy issue which is
|
||||||
|
# worked around using a somewhat complicated _ListOrTuple union type. Once the
|
||||||
|
# issue is solved upstream this test should pass even with the workaround
|
||||||
|
# replaced by a simpler Sequence type.
|
||||||
|
# https://github.com/python/mypy/issues/8921
|
||||||
|
- case: test_fieldset_workaround_regression
|
||||||
|
main: |
|
||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
class A(admin.ModelAdmin):
|
||||||
|
fieldsets = (
|
||||||
|
(None, {
|
||||||
|
'fields': ('name',),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
- case: errors_on_omitting_fields_from_fieldset_opts
|
||||||
|
main: |
|
||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
class A(admin.ModelAdmin):
|
||||||
|
fieldsets = [ # type: ignore
|
||||||
|
(None, {}), # E: Key 'fields' missing for TypedDict "_FieldOpts"
|
||||||
|
]
|
||||||
|
- case: errors_on_invalid_radio_fields
|
||||||
|
main: |
|
||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
class A(admin.ModelAdmin):
|
||||||
|
radio_fields = {"some_field": 0} # E: Dict entry 0 has incompatible type "str": "Literal[0]"; expected "str": "Union[Literal[1], Literal[2]]"
|
||||||
|
|
||||||
|
class B(admin.ModelAdmin):
|
||||||
|
radio_fields = {1: admin.VERTICAL} # E: Dict entry 0 has incompatible type "int": "Literal[2]"; expected "str": "Union[Literal[1], Literal[2]]"
|
||||||
|
- case: errors_for_invalid_formfield_overrides
|
||||||
|
main: |
|
||||||
|
from django.contrib import admin
|
||||||
|
from django.forms import Textarea
|
||||||
|
|
||||||
|
class A(admin.ModelAdmin):
|
||||||
|
formfield_overrides = {
|
||||||
|
"not a field": { # E: Dict entry 0 has incompatible type "str": "Dict[str, Any]"; expected "Type[Field[Any, Any]]": "Mapping[str, Any]"
|
||||||
|
"widget": Textarea
|
||||||
|
}
|
||||||
|
}
|
||||||
|
- case: errors_for_invalid_action_signature
|
||||||
|
main: |
|
||||||
|
from django.contrib import admin
|
||||||
|
from django.http.request import HttpRequest
|
||||||
|
from django.db.models.query import QuerySet
|
||||||
|
|
||||||
|
def an_action(modeladmin: None) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
class A(admin.ModelAdmin):
|
||||||
|
actions = [an_action] # E: List item 0 has incompatible type "Callable[[None], None]"; expected "Union[Callable[[ModelAdmin, HttpRequest, QuerySet[Any]], None], str]"
|
||||||
43
test-data/typecheck/contrib/auth/test_decorators.yml
Normal file
43
test-data/typecheck/contrib/auth/test_decorators.yml
Normal 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): ...
|
||||||
26
test-data/typecheck/db/models/test_init.yml
Normal file
26
test-data/typecheck/db/models/test_init.yml
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
- case: field_to_many_and_to_one_attrs_bool_or_none_in_field_base_class
|
||||||
|
main: |
|
||||||
|
from django.db.models import Field
|
||||||
|
|
||||||
|
field: Field
|
||||||
|
my_bool: bool
|
||||||
|
|
||||||
|
my_bool = field.one_to_many
|
||||||
|
my_bool = field.one_to_one
|
||||||
|
my_bool = field.many_to_many
|
||||||
|
my_bool = field.many_to_one
|
||||||
|
|
||||||
|
# Narrowing the types should give us bool
|
||||||
|
assert field.one_to_many is not None
|
||||||
|
my_bool = field.one_to_many
|
||||||
|
assert field.one_to_one is not None
|
||||||
|
my_bool = field.one_to_one
|
||||||
|
assert field.many_to_many is not None
|
||||||
|
my_bool = field.many_to_many
|
||||||
|
assert field.many_to_one is not None
|
||||||
|
my_bool = field.many_to_one
|
||||||
|
out: |
|
||||||
|
main:6: error: Incompatible types in assignment (expression has type "Optional[bool]", variable has type "bool")
|
||||||
|
main:7: error: Incompatible types in assignment (expression has type "Optional[bool]", variable has type "bool")
|
||||||
|
main:8: error: Incompatible types in assignment (expression has type "Optional[bool]", variable has type "bool")
|
||||||
|
main:9: error: Incompatible types in assignment (expression has type "Optional[bool]", variable has type "bool")
|
||||||
28
test-data/typecheck/db/test_transaction.yml
Normal file
28
test-data/typecheck/db/test_transaction.yml
Normal 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'
|
||||||
|
|
||||||
@@ -335,3 +335,25 @@
|
|||||||
objects = MyManager()
|
objects = MyManager()
|
||||||
class ChildUser(models.Model):
|
class ChildUser(models.Model):
|
||||||
objects = MyManager()
|
objects = MyManager()
|
||||||
|
|
||||||
|
- case: custom_manager_annotate_method_before_type_declaration
|
||||||
|
main: |
|
||||||
|
from myapp.models import ModelA, ModelB, ManagerA
|
||||||
|
reveal_type(ModelA.objects) # N: Revealed type is 'myapp.models.ModelA_ManagerA1[myapp.models.ModelA]'
|
||||||
|
reveal_type(ModelA.objects.do_something) # N: Revealed type is 'def (other_obj: myapp.models.ModelB) -> builtins.str'
|
||||||
|
installed_apps:
|
||||||
|
- myapp
|
||||||
|
files:
|
||||||
|
- path: myapp/__init__.py
|
||||||
|
- path: myapp/models.py
|
||||||
|
content: |
|
||||||
|
from django.db import models
|
||||||
|
class ManagerA(models.Manager):
|
||||||
|
def do_something(self, other_obj: "ModelB") -> str:
|
||||||
|
return 'test'
|
||||||
|
class ModelA(models.Model):
|
||||||
|
title = models.TextField()
|
||||||
|
objects = ManagerA()
|
||||||
|
class ModelB(models.Model):
|
||||||
|
movie = models.TextField()
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,12 @@
|
|||||||
reveal_type(User().is_active) # N: Revealed type is 'builtins.bool*'
|
reveal_type(User().is_active) # N: Revealed type is 'builtins.bool*'
|
||||||
reveal_type(User().date_joined) # N: Revealed type is 'datetime.datetime*'
|
reveal_type(User().date_joined) # N: Revealed type is 'datetime.datetime*'
|
||||||
reveal_type(User().last_login) # N: Revealed type is 'Union[datetime.datetime, None]'
|
reveal_type(User().last_login) # N: Revealed type is 'Union[datetime.datetime, None]'
|
||||||
|
reveal_type(User().is_authenticated) # N: Revealed type is 'Literal[True]'
|
||||||
|
reveal_type(User().is_anonymous) # N: Revealed type is 'Literal[False]'
|
||||||
|
|
||||||
|
from django.contrib.auth.models import AnonymousUser
|
||||||
|
reveal_type(AnonymousUser().is_authenticated) # N: Revealed type is 'Literal[False]'
|
||||||
|
reveal_type(AnonymousUser().is_anonymous) # N: Revealed type is 'Literal[True]'
|
||||||
|
|
||||||
from django.contrib.auth.models import Permission
|
from django.contrib.auth.models import Permission
|
||||||
reveal_type(Permission().name) # N: Revealed type is 'builtins.str*'
|
reveal_type(Permission().name) # N: Revealed type is 'builtins.str*'
|
||||||
|
|||||||
14
test-data/typecheck/models/test_state.yml
Normal file
14
test-data/typecheck/models/test_state.yml
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
- case: state_attribute_has_a_type_of_model_state
|
||||||
|
main: |
|
||||||
|
from myapp.models import MyUser
|
||||||
|
user = MyUser(pk=1)
|
||||||
|
reveal_type(user._state) # N: Revealed type is 'django.db.models.base.ModelState'
|
||||||
|
installed_apps:
|
||||||
|
- myapp
|
||||||
|
files:
|
||||||
|
- path: myapp/__init__.py
|
||||||
|
- path: myapp/models.py
|
||||||
|
content: |
|
||||||
|
from django.db import models
|
||||||
|
class MyUser(models.Model):
|
||||||
|
pass
|
||||||
12
test-data/typecheck/test/test_testcase.yml
Normal file
12
test-data/typecheck/test/test_testcase.yml
Normal 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'
|
||||||
@@ -46,6 +46,18 @@
|
|||||||
reveal_type(self.get_form(form_class)) # N: Revealed type is 'main.MyForm'
|
reveal_type(self.get_form(form_class)) # N: Revealed type is 'main.MyForm'
|
||||||
reveal_type(self.get_form(MyForm2)) # N: Revealed type is 'main.MyForm2'
|
reveal_type(self.get_form(MyForm2)) # N: Revealed type is 'main.MyForm2'
|
||||||
|
|
||||||
|
- case: updateview_form_valid_has_form_save
|
||||||
|
main: |
|
||||||
|
from django import forms
|
||||||
|
from django.views.generic.edit import UpdateView
|
||||||
|
|
||||||
|
class MyForm(forms.ModelForm):
|
||||||
|
pass
|
||||||
|
class MyView(UpdateView):
|
||||||
|
form_class = MyForm
|
||||||
|
def form_valid(self, form: forms.BaseModelForm):
|
||||||
|
reveal_type(form.save) # N: Revealed type is 'def (commit: builtins.bool =) -> Any'
|
||||||
|
|
||||||
- case: successmessagemixin_compatible_with_formmixin
|
- case: successmessagemixin_compatible_with_formmixin
|
||||||
main: |
|
main: |
|
||||||
from django.views.generic.edit import FormMixin
|
from django.views.generic.edit import FormMixin
|
||||||
|
|||||||
@@ -2,11 +2,11 @@
|
|||||||
disable_cache: true
|
disable_cache: true
|
||||||
main: |
|
main: |
|
||||||
from django.http.request import HttpRequest
|
from django.http.request import HttpRequest
|
||||||
reveal_type(HttpRequest().user) # N: Revealed type is 'myapp.models.MyUser'
|
reveal_type(HttpRequest().user) # N: Revealed type is 'Union[myapp.models.MyUser, django.contrib.auth.models.AnonymousUser]'
|
||||||
# check that other fields work ok
|
# check that other fields work ok
|
||||||
reveal_type(HttpRequest().method) # N: Revealed type is 'Union[builtins.str, None]'
|
reveal_type(HttpRequest().method) # N: Revealed type is 'Union[builtins.str, None]'
|
||||||
custom_settings: |
|
custom_settings: |
|
||||||
INSTALLED_APPS = ('django.contrib.contenttypes', 'myapp')
|
INSTALLED_APPS = ('django.contrib.contenttypes', 'django.contrib.auth', 'myapp')
|
||||||
AUTH_USER_MODEL='myapp.MyUser'
|
AUTH_USER_MODEL='myapp.MyUser'
|
||||||
files:
|
files:
|
||||||
- path: myapp/__init__.py
|
- path: myapp/__init__.py
|
||||||
@@ -14,4 +14,41 @@
|
|||||||
content: |
|
content: |
|
||||||
from django.db import models
|
from django.db import models
|
||||||
class MyUser(models.Model):
|
class MyUser(models.Model):
|
||||||
pass
|
pass
|
||||||
|
- case: request_object_user_can_be_descriminated
|
||||||
|
disable_cache: true
|
||||||
|
main: |
|
||||||
|
from django.http.request import HttpRequest
|
||||||
|
request = HttpRequest()
|
||||||
|
reveal_type(request.user) # N: Revealed type is 'Union[django.contrib.auth.models.User, django.contrib.auth.models.AnonymousUser]'
|
||||||
|
if not request.user.is_anonymous:
|
||||||
|
reveal_type(request.user) # N: Revealed type is 'django.contrib.auth.models.User'
|
||||||
|
if request.user.is_authenticated:
|
||||||
|
reveal_type(request.user) # N: Revealed type is 'django.contrib.auth.models.User'
|
||||||
|
custom_settings: |
|
||||||
|
INSTALLED_APPS = ('django.contrib.contenttypes', 'django.contrib.auth')
|
||||||
|
- case: subclass_request_not_changed_user_type
|
||||||
|
disable_cache: true
|
||||||
|
main: |
|
||||||
|
from django.http.request import HttpRequest
|
||||||
|
class MyRequest(HttpRequest):
|
||||||
|
foo: int # Just do something
|
||||||
|
|
||||||
|
request = MyRequest()
|
||||||
|
reveal_type(request.user) # N: Revealed type is 'Union[django.contrib.auth.models.User, django.contrib.auth.models.AnonymousUser]'
|
||||||
|
custom_settings: |
|
||||||
|
INSTALLED_APPS = ('django.contrib.contenttypes', 'django.contrib.auth')
|
||||||
|
|
||||||
|
- case: subclass_request_changed_user_type
|
||||||
|
disable_cache: true
|
||||||
|
main: |
|
||||||
|
from django.http.request import HttpRequest
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
class MyRequest(HttpRequest):
|
||||||
|
user: User # Override the type of user
|
||||||
|
|
||||||
|
request = MyRequest()
|
||||||
|
reveal_type(request.user) # N: Revealed type is 'django.contrib.auth.models.User'
|
||||||
|
custom_settings: |
|
||||||
|
INSTALLED_APPS = ('django.contrib.contenttypes', 'django.contrib.auth')
|
||||||
|
|
||||||
|
|||||||
@@ -36,3 +36,24 @@
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
class MyUser(models.Model):
|
class MyUser(models.Model):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
- case: check_render_function_arguments_annotations
|
||||||
|
main: |
|
||||||
|
from typing import Any
|
||||||
|
from typing_extensions import TypedDict
|
||||||
|
from django.shortcuts import render
|
||||||
|
from django.http.request import HttpRequest
|
||||||
|
|
||||||
|
TestContext = TypedDict("TestContext", {"user": Any})
|
||||||
|
test_context: TestContext = {"user": "test"}
|
||||||
|
reveal_type(test_context) # N: Revealed type is 'TypedDict('main.TestContext', {'user': Any})'
|
||||||
|
reveal_type(render(HttpRequest(), '', test_context)) # N: Revealed type is 'django.http.response.HttpResponse'
|
||||||
|
|
||||||
|
- case: check_redirect_return_annotation
|
||||||
|
main: |
|
||||||
|
from django.shortcuts import redirect
|
||||||
|
reveal_type(redirect(to = '', permanent = True)) # N: Revealed type is 'django.http.response.HttpResponsePermanentRedirect'
|
||||||
|
reveal_type(redirect(to = '', permanent = False)) # N: Revealed type is 'django.http.response.HttpResponseRedirect'
|
||||||
|
|
||||||
|
var = True
|
||||||
|
reveal_type(redirect(to = '', permanent = var)) # N: Revealed type is 'Union[django.http.response.HttpResponseRedirect, django.http.response.HttpResponsePermanentRedirect]'
|
||||||
|
|||||||
20
test-data/typecheck/utils/test_decorators.yml
Normal file
20
test-data/typecheck/utils/test_decorators.yml
Normal 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'
|
||||||
73
test-plugin/test_error_handling.py
Normal file
73
test-plugin/test_error_handling.py
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
import tempfile
|
||||||
|
import typing
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from mypy_django_plugin.main import extract_django_settings_module
|
||||||
|
|
||||||
|
TEMPLATE = """usage: (config)
|
||||||
|
...
|
||||||
|
[mypy.plugins.django_stubs]
|
||||||
|
django_settings_module: str (required)
|
||||||
|
...
|
||||||
|
(django-stubs) mypy: error: 'django_settings_module' is not set: {}
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'config_file_contents,message_part',
|
||||||
|
[
|
||||||
|
pytest.param(
|
||||||
|
None,
|
||||||
|
'mypy config file is not specified or found',
|
||||||
|
id='missing-file',
|
||||||
|
),
|
||||||
|
pytest.param(
|
||||||
|
['[not-really-django-stubs]'],
|
||||||
|
'no section [mypy.plugins.django-stubs]',
|
||||||
|
id='missing-section',
|
||||||
|
),
|
||||||
|
pytest.param(
|
||||||
|
['[mypy.plugins.django-stubs]',
|
||||||
|
'\tnot_django_not_settings_module = badbadmodule'],
|
||||||
|
'the setting is not provided',
|
||||||
|
id='missing-settings-module',
|
||||||
|
),
|
||||||
|
pytest.param(
|
||||||
|
['[mypy.plugins.django-stubs]'],
|
||||||
|
'the setting is not provided',
|
||||||
|
id='no-settings-given',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_misconfiguration_handling(capsys, config_file_contents, message_part):
|
||||||
|
# type: (typing.Any, typing.List[str], str) -> None
|
||||||
|
"""Invalid configuration raises `SystemExit` with a precise error message."""
|
||||||
|
with tempfile.NamedTemporaryFile(mode='w+') as config_file:
|
||||||
|
if not config_file_contents:
|
||||||
|
config_file.close()
|
||||||
|
else:
|
||||||
|
config_file.write('\n'.join(config_file_contents).expandtabs(4))
|
||||||
|
config_file.seek(0)
|
||||||
|
|
||||||
|
with pytest.raises(SystemExit, match='2'):
|
||||||
|
extract_django_settings_module(config_file.name)
|
||||||
|
|
||||||
|
error_message = TEMPLATE.format(message_part)
|
||||||
|
assert error_message == capsys.readouterr().err
|
||||||
|
|
||||||
|
|
||||||
|
def test_correct_configuration() -> None:
|
||||||
|
"""Django settings module gets extracted given valid configuration."""
|
||||||
|
config_file_contents = [
|
||||||
|
'[mypy.plugins.django-stubs]',
|
||||||
|
'\tsome_other_setting = setting',
|
||||||
|
'\tdjango_settings_module = my.module',
|
||||||
|
]
|
||||||
|
with tempfile.NamedTemporaryFile(mode='w+') as config_file:
|
||||||
|
config_file.write('\n'.join(config_file_contents).expandtabs(4))
|
||||||
|
config_file.seek(0)
|
||||||
|
|
||||||
|
extracted = extract_django_settings_module(config_file.name)
|
||||||
|
|
||||||
|
assert extracted == 'my.module'
|
||||||
Reference in New Issue
Block a user