54 Commits

Author SHA1 Message Date
Nikita Sobolev
3d2534ea8d Closes #392 2020-06-08 10:55:30 +03:00
Kacper
54f5f63e71 Issue 379 (#391)
* pytest-mypy-plugins package newer version

* Revert "pytest-mypy-plugins package newer version"

This reverts commit 871347a86577a5dad867bc751689bbc06d2bcae0.

* update tests ignores for django

* Revert "update tests ignores for django"

This reverts commit 93fc66e311af62cf8cf5b79a72ab723bf3cf060a.

* tests for python 3.8 WIP - initial commit

Co-authored-by: Kacper Szmigiel <szmigielkacper@gmai.com>
2020-06-06 14:28:28 +03:00
Kacper
4c5723d368 WIP Issue 388 (#390)
* updated mypy dependency

* update readme

* readme update v2

* pytest-mypy-plugins newer version

* updated pytest_mypy_plugins name

* update ignored errors for typechecking django test suite

Co-authored-by: Kacper Szmigiel <szmigielkacper@gmai.com>
2020-06-06 11:35:51 +03:00
Alexander Viklund
7e0e43135d BaseForm.prefix may be None (#389) 2020-06-04 21:24:42 +03:00
Kacper
e05b84e32d Issue 378 (#387)
* proper redirect return type annotations made with Literal

* Mapping instead of Dict type annotation for context in render() with test

* removed Union and Context

* typo

Co-authored-by: Kacper Szmigiel <szmigielkacper@gmai.com>

Add __init__ to OrderedSet (#381)

Issue 382 (#384)

* WIP fix, pushed for testing

* added _ prefix for internal types

Co-authored-by: Kacper Szmigiel <szmigielkacper@gmai.com>

Fix parameter types for assertJSONEqual/NotEqual (#385)

Add get_supported_language_variant (#386)

Issue 309 (#383)

* added tags for user models

* type test for HttpRequest.user

* test for User and AnonymousUser tags

* httrequest test fix

* checking python version fix for readibility

* Rewrite version check for readability

* Annotate is_authenticated/is_anonymous with Literal-type

* Add auth in INSTALLED_APPS in test

* Fix wrong type assertion in test

* Fix misconception of how branch-testing works

* Remove user from WSGIRequest

* Change HttpRequest-transformer to set user-type to include AnonymousUser

* Add check for anonymous_user_info=None to appease mypy

* Isort transformers/request

* Remove trailing whitespace

* Remove unused import

Co-authored-by: Kacper Szmigiel <szmigielkacper@gmai.com>

* fix formatting and unused import

* reformatted again

Co-authored-by: Kacper Szmigiel <szmigielkacper@gmai.com>
2020-06-03 23:46:30 +03:00
Alexander Viklund
71751d3795 Issue 309 (#383)
* added tags for user models

* type test for HttpRequest.user

* test for User and AnonymousUser tags

* httrequest test fix

* checking python version fix for readibility

* Rewrite version check for readability

* Annotate is_authenticated/is_anonymous with Literal-type

* Add auth in INSTALLED_APPS in test

* Fix wrong type assertion in test

* Fix misconception of how branch-testing works

* Remove user from WSGIRequest

* Change HttpRequest-transformer to set user-type to include AnonymousUser

* Add check for anonymous_user_info=None to appease mypy

* Isort transformers/request

* Remove trailing whitespace

* Remove unused import

Co-authored-by: Kacper Szmigiel <szmigielkacper@gmai.com>
2020-06-03 20:29:19 +03:00
Alexander Viklund
25f92e8e56 Add get_supported_language_variant (#386) 2020-06-03 20:04:19 +03:00
Alexander Viklund
28d47c7e93 Fix parameter types for assertJSONEqual/NotEqual (#385) 2020-06-03 20:03:59 +03:00
Kacper
197cb4058e Issue 382 (#384)
* WIP fix, pushed for testing

* added _ prefix for internal types

Co-authored-by: Kacper Szmigiel <szmigielkacper@gmai.com>
2020-06-03 12:58:03 +03:00
Alexander Viklund
dac2b31fb2 Add __init__ to OrderedSet (#381) 2020-06-02 13:56:11 +03:00
Kacper
8d2600136a Issue 355 (#376)
* Mapping instead of Dict type annotation for context in render() with test

* removed Union and Context

* typo

Co-authored-by: Kacper Szmigiel <szmigielkacper@gmai.com>
2020-06-01 19:41:57 +03:00
Pavel Savchenko
570772f973 Revert "Allow template render to string helper functions to accept Context (#372)" (#377)
This reverts commit 64cbb0f70e.
2020-06-01 14:34:21 +03:00
Anton Agestam
d5c1bfb12a Increase accuracy of ModelAdmin types (#375)
* Increase accuracy of ModelAdmin types

* Add comment for regression test
2020-05-30 00:24:47 +03:00
Pavel Savchenko
64cbb0f70e Allow template render to string helper functions to accept Context (#372)
* Add failing test case for render_to_string

* Allow Context objects in template render functions
2020-05-23 13:47:39 +03:00
Ville Skyttä
6f5a39625e Add release notes project URL (#365)
Background info at https://github.com/pypa/warehouse/pull/7882
2020-05-05 18:38:11 +03:00
Anthony Ricaud
bf604a0398 Add BaseForm._html_output (#364) 2020-04-30 14:36:55 +03:00
Ashish Bansal (mrphantom)
92c8dfc93f Fix wrong BaseCache typings (#363)
django.core.cache.backends.base.BaseCache were invalid. This commit
fixes them in accordance with [0].

[0] 447980e72a/django/core/cache/backends/base.py (L108)
2020-04-24 12:44:55 +03:00
Jorgen Phillips
c10c55052c Add add_to_class method to Model (#361)
* Add add_to_class method to Model

* Fix @classmethod decorator
2020-04-24 01:11:16 +03:00
Sergey Tikhonov
96914e466b Annotate AdminForm.__init__ (#359) 2020-04-16 18:22:21 +03:00
Onyeka Aghanenu
90ed7f332d Fix Modelform auto_id to accept Boolean type. (#356) 2020-04-13 19:04:18 +03:00
Kacper
a801501151 stub for _build_app_dict (#351)
Co-authored-by: Kacper Szmigiel <szmigielkacper@gmai.com>
2020-04-04 20:38:42 +03:00
Sid Mitra
8ea59985df Add RESTRICT and RestrictedError to django.db.models.deletion (#345)
* Add RESTRICT and RestrictedError to django.db.models.deletion

* Add black fomatting changes
2020-04-03 17:02:34 +03:00
Ceesjan Luiten
2964ed53d7 Improve type of BaseForms.files (#350)
This is valid Django, which failed to pass with the old type-defintion:

    class MyForm(Form):
      myparam = MultiFileField(...)

      self.clean(self, *args, **kwargs):
        self.files.getlist('myparam', [])
2020-03-31 10:16:02 +03:00
Stevan Milic
1b9176f994 Add state attribute to Model (#347) (#348)
Add from_db method to Model

Co-authored-by: Stevan Milic <stevan.milic@tradecore.com>
2020-03-30 10:35:40 +03:00
Nikita Sobolev
54d0d018c6 Minor type improvements after linting (#343) 2020-03-19 18:04:51 +03:00
Maxim Kurnikov
1af3a12f2c bump to 1.5.0 2020-03-15 00:59:58 +03:00
Marti Raudsepp
7af89ee6a6 Update to mypy 0.770 (#341) 2020-03-13 16:45:45 +03:00
Marti Raudsepp
afa16bfb74 Make decorator functions transparent to Mypy (#306)
By declaring return type as -> Callable[[_C], _C], Mypy can infer that
the decorated function has also the same arguments and return type as
the original.

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

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

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

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

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

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

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

Closes #206 
Refs #274

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

* Update README.md

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

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

* It should not fail right now 🤔

* Refactor validators a bit

* Fixes CI

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

* only anal_type per argument

* add resolve for return_type

* fix mypy errors

* remove leftover comment
2019-12-18 20:01:20 +03:00
Maksim Kurnikov
38135f2d1f Merge pull request #281 from mkurnikov/update-deps
Update dev deps, mypy to 0.760
2019-12-18 00:13:11 +03:00
Maxim Kurnikov
998b659749 bump to 1.4.0 2019-12-18 00:03:29 +03:00
Maxim Kurnikov
72f69e1c5e remove unused ignores 2019-12-18 00:02:55 +03:00
Maxim Kurnikov
d666ecd36f update dev deps, mypy to 0.760 2019-12-17 23:50:50 +03:00
Maksim Kurnikov
c1af26c027 handle return value of anal_type properly (#280) 2019-12-17 23:36:44 +03:00
62 changed files with 956 additions and 280 deletions

View File

@@ -4,10 +4,19 @@ dist: xenial
sudo: required
jobs:
include:
- name: Run plugin test suite with python 3.8
python: 3.8
script: 'pytest'
- name: Run plugin test suite with python 3.7
python: 3.7
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
python: 3.7
script: |

111
CONTRIBUTING.md Normal file
View File

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

113
README.md
View File

@@ -1,14 +1,12 @@
<img src="http://mypy-lang.org/static/mypy_light.svg" alt="mypy logo" width="300px"/>
# pep484 stubs for Django framework
# Typesafe Django Framework
[![Build Status](https://travis-ci.com/typeddjango/django-stubs.svg?branch=master)](https://travis-ci.com/typeddjango/django-stubs)
[![Checked with mypy](http://www.mypy-lang.org/static/mypy_badge.svg)](http://mypy-lang.org/)
[![Gitter](https://badges.gitter.im/mypy-django/Lobby.svg)](https://gitter.im/mypy-django/Lobby)
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.
Could be run on earlier versions of Django, but expect some missing imports warnings.
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.
## Installation
@@ -17,69 +15,106 @@ Could be run on earlier versions of Django, but expect some missing imports warn
pip install django-stubs
```
## 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
See [Configuration](#configuration) section to get started.
## Configuration
To make mypy aware of the plugin, you need to add
To make `mypy` happy, you will need to add:
```ini
[mypy]
plugins =
mypy_django_plugin.main
[mypy.plugins.django-stubs]
django_settings_module = "myproject.settings"
```
in your `mypy.ini` or `setup.cfg` [file](https://mypy.readthedocs.io/en/latest/config_file.html).
Plugin also requires Django settings module (what you put into `DJANGO_SETTINGS_MODULE` variable) to be specified.
Two things happeining here:
```ini
[mypy]
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.
1. We need to explicitly list our plugin to be loaded by `mypy`
2. Our plugin also requires `django` settings module (what you put into `DJANGO_SETTINGS_MODULE` variable) to be specified
This fully working [typed boilerplate](https://github.com/wemake-services/wemake-django-template) can serve you as an example.
## Notes
## Version compatibility
Type implementation monkey-patches Django to add `__class_getitem__` to the `Manager` class.
If you would use Python3.7 and do that too in your code, you can make things like
We rely on different `django` and `mypy` versions:
| django-stubs | mypy version | django version | python version
| ------------ | ---- | ---- | ---- |
| 1.5.0 | 0.780 | 2.2.x \|\| 3.x | ^3.6
| 1.4.0 | 0.770 | 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 use HttpRequest with custom user model?
You can subclass standard request like so:
```python
class MyUserManager(models.Manager['MyUser']):
pass
from django.http import HttpRequest
from my_user_app.models import MyUser
class MyUser(models.Model):
objects = MyUserManager()
class MyRequest(HttpRequest):
user: MyUser
```
work, which should make a error messages a bit better.
And then use `MyRequest` instead of standard `HttpRequest` inside your project.
## Related projects
- [`awesome-python-typing`](https://github.com/typeddjango/awesome-python-typing) - Awesome list of all typing-related things in Python.
- [`djangorestframework-stubs`](https://github.com/typeddjango/djangorestframework-stubs) - Stubs for Django REST Framework.
- [`pytest-mypy-plugins`](https://github.com/typeddjango/pytest-mypy-plugins) - `pytest` plugin that we use for testing `mypy` stubs and plugins.
- [`wemake-django-template`](https://github.com/wemake-services/wemake-django-template) - Create new typed Django projects in seconds.
Otherwise, custom type will be created in mypy, named `MyUser__MyUserManager`, which will rewrite base manager as `models.Manager[User]` to make methods like `get_queryset()` and others return properly typed `QuerySet`.
## To get help
We have Gitter here: <https://gitter.im/mypy-django/Lobby>
If you think you have more generic typing issue, please refer to https://github.com/python/mypy and their Gitter.
If you think you have more generic typing issue, please refer to <https://github.com/python/mypy> and their Gitter.

View File

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

View File

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

View File

@@ -1,7 +1,7 @@
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.forms import BaseForm
from django.forms.utils import ErrorDict
from django.forms.widgets import Media, Widget
from django.utils.safestring import SafeText
@@ -23,7 +23,7 @@ class AdminForm:
readonly_fields: Any = ...
def __init__(
self,
form: AdminPasswordChangeForm,
form: BaseForm,
fieldsets: List[Tuple[None, Dict[str, List[str]]]],
prepopulated_fields: Dict[Any, Any],
readonly_fields: Optional[Iterable[Any]] = ...,

View File

@@ -1,5 +1,9 @@
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.models import LogEntry
@@ -26,8 +30,10 @@ from django.db.models.fields import Field
IS_POPUP_VAR: str
TO_FIELD_VAR: str
HORIZONTAL: Any
VERTICAL: Any
HORIZONTAL: Literal[1] = ...
VERTICAL: Literal[2] = ...
_Direction = Union[Literal[1], Literal[2]]
def get_content_type_for_model(obj: Union[Type[Model], Model]) -> ContentType: ...
def get_ul_class(radio_style: int) -> str: ...
@@ -37,21 +43,35 @@ class IncorrectLookupParameters(Exception): ...
FORMFIELD_FOR_DBFIELD_DEFAULTS: 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:
autocomplete_fields: Any = ...
raw_id_fields: Any = ...
fields: Any = ...
exclude: Any = ...
fieldsets: Any = ...
form: Any = ...
filter_vertical: Any = ...
filter_horizontal: Any = ...
radio_fields: Any = ...
prepopulated_fields: Any = ...
formfield_overrides: Any = ...
readonly_fields: Any = ...
ordering: Any = ...
sortable_by: Any = ...
autocomplete_fields: Sequence[str] = ...
raw_id_fields: Sequence[str] = ...
fields: Sequence[Union[str, Sequence[str]]] = ...
exclude: Sequence[str] = ...
fieldsets: _FieldsetSpec = ...
form: Type[BaseForm] = ...
filter_vertical: Sequence[str] = ...
filter_horizontal: Sequence[str] = ...
radio_fields: Mapping[str, _Direction] = ...
prepopulated_fields: Mapping[str, Sequence[str]] = ...
formfield_overrides: Mapping[Type[Field], Mapping[str, Any]] = ...
readonly_fields: Sequence[Union[str, Callable[[Model], Any]]] = ...
ordering: Sequence[str] = ...
sortable_by: Sequence[str] = ...
view_on_site: bool = ...
show_full_result_count: bool = ...
checks_class: Any = ...
@@ -93,7 +113,7 @@ class BaseModelAdmin:
def has_module_permission(self, request: HttpRequest) -> bool: ...
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_filter: Sequence[Union[str, Type[ListFilter], Tuple[str, Type[ListFilter]]]] = ...
list_select_related: Union[bool, Sequence[str]] = ...
@@ -101,21 +121,21 @@ class ModelAdmin(BaseModelAdmin):
list_max_show_all: int = ...
list_editable: Sequence[str] = ...
search_fields: Sequence[str] = ...
date_hierarchy: Optional[Any] = ...
date_hierarchy: Optional[str] = ...
save_as: bool = ...
save_as_continue: bool = ...
save_on_top: bool = ...
paginator: Any = ...
paginator: Type = ...
preserve_filters: bool = ...
inlines: Sequence[Type[InlineModelAdmin]] = ...
add_form_template: Any = ...
change_form_template: Any = ...
change_list_template: Any = ...
delete_confirmation_template: Any = ...
delete_selected_confirmation_template: Any = ...
object_history_template: Any = ...
popup_response_template: Any = ...
actions: Any = ...
add_form_template: str = ...
change_form_template: str = ...
change_list_template: str = ...
delete_confirmation_template: str = ...
delete_selected_confirmation_template: str = ...
object_history_template: str = ...
popup_response_template: str = ...
actions: Sequence[Callable[[ModelAdmin, HttpRequest, QuerySet], None]] = ...
action_form: Any = ...
actions_on_top: 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: ...
class InlineModelAdmin(BaseModelAdmin):
model: Any = ...
fk_name: Any = ...
formset: Any = ...
model: Type[Model] = ...
fk_name: str = ...
formset: BaseFormSet = ...
extra: int = ...
min_num: Optional[int] = ...
max_num: Optional[int] = ...
@@ -238,8 +258,8 @@ class InlineModelAdmin(BaseModelAdmin):
verbose_name_plural: Optional[str] = ...
can_delete: bool = ...
show_change_link: bool = ...
classes: Any = ...
admin_site: Any = ...
classes: Optional[Sequence[str]] = ...
admin_site: AdminSite = ...
parent_model: Any = ...
opts: Any = ...
has_registered_model: Any = ...

View File

@@ -63,6 +63,7 @@ class AdminSite:
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 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 index(self, request: WSGIRequest, extra_context: Optional[Dict[str, Any]] = ...) -> TemplateResponse: ...
def app_index(

View File

@@ -1,9 +1,15 @@
import sys
from typing import Any, Optional, Tuple, List, overload, TypeVar
from django.db.models.base import Model
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)
class BaseUserManager(models.Manager[_T]):
@@ -20,9 +26,9 @@ class AbstractBaseUser(models.Model):
def get_username(self) -> str: ...
def natural_key(self) -> Tuple[str]: ...
@property
def is_anonymous(self) -> bool: ...
def is_anonymous(self) -> Literal[False]: ...
@property
def is_authenticated(self) -> bool: ...
def is_authenticated(self) -> Literal[True]: ...
def set_password(self, raw_password: Optional[str]) -> None: ...
def check_password(self, raw_password: str) -> bool: ...
def set_unusable_password(self) -> None: ...

View File

@@ -1,13 +1,21 @@
from typing import Callable, List, Optional, Set, Union
from typing import Callable, List, Optional, Set, Union, TypeVar, overload
from django.contrib.auth import REDIRECT_FIELD_NAME as REDIRECT_FIELD_NAME # noqa: F401
from django.http.response import HttpResponseBase
from django.contrib.auth.models import AbstractUser
_VIEW = TypeVar("_VIEW", bound=Callable[..., HttpResponseBase])
def user_passes_test(
test_func: Callable, login_url: Optional[str] = ..., redirect_field_name: str = ...
) -> Callable: ...
def login_required(
function: Optional[Callable] = ..., redirect_field_name: str = ..., login_url: Optional[str] = ...
) -> Callable: ...
test_func: Callable[[AbstractUser], bool], login_url: Optional[str] = ..., redirect_field_name: str = ...
) -> Callable[[_VIEW], _VIEW]: ...
# There are two ways of calling @login_required: @with(arguments) and @bare
@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(
perm: Union[List[str], Set[str], str], login_url: None = ..., raise_exception: bool = ...
) -> Callable: ...
) -> Callable[[_VIEW], _VIEW]: ...

View File

@@ -1,3 +1,4 @@
import sys
from typing import Any, Collection, Optional, Set, Tuple, Type, TypeVar, Union
from django.contrib.auth.backends import ModelBackend
@@ -9,6 +10,11 @@ from django.db.models.manager import EmptyManager
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"]
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_module_perms(self, module: str) -> bool: ...
@property
def is_anonymous(self) -> bool: ...
def is_anonymous(self) -> Literal[True]: ...
@property
def is_authenticated(self) -> bool: ...
def is_authenticated(self) -> Literal[False]: ...
def get_username(self) -> str: ...

View File

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

View File

@@ -1,5 +1,5 @@
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.requests import RequestSite
@@ -13,10 +13,19 @@ class SitemapNotFound(Exception): ...
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:
limit: int = ...
protocol: Optional[str] = ...
def items(self) -> List[Any]: ...
def items(self) -> Union[_SupportsLen, _SupportsCount, _SupportsOrdered]: ...
def location(self, obj: Model) -> str: ...
@property
def paginator(self) -> Paginator: ...

View File

@@ -19,16 +19,16 @@ class BaseCache:
def __init__(self, params: Dict[str, Any]) -> None: ...
def get_backend_timeout(self, timeout: Any = ...) -> Optional[float]: ...
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 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 get_many(self, keys: List[str], version: Optional[int] = ...) -> Dict[str, Union[int, str]]: ...
def get_or_set(
self, key: Any, default: Optional[Any], timeout: Any = ..., version: Optional[int] = ...
) -> 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 decr(self, key: str, delta: int = ..., version: Optional[int] = ...) -> int: ...
def __contains__(self, key: str) -> bool: ...

View File

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

View File

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

View File

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

View File

@@ -15,14 +15,14 @@ class SkipFile(UploadFileException): ...
class StopFutureHandlers(UploadFileException): ...
class FileUploadHandler:
chunk_size = ... # type: int
file_name = ... # type: Optional[str]
content_type = ... # type: Optional[str]
content_length = ... # type: Optional[int]
charset = ... # type: Optional[str]
content_type_extra = ... # type: Optional[Dict[str, str]]
request = ... # type: Optional[HttpRequest]
field_name = ... # type: str
chunk_size: int = ...
file_name: Optional[str] = ...
content_type: Optional[str] = ...
content_length: Optional[int] = ...
charset: Optional[str] = ...
content_type_extra: Optional[Dict[str, str]] = ...
request: Optional[HttpRequest] = ...
field_name: str = ...
def __init__(self, request: Optional[HttpRequest] = ...) -> None: ...
def handle_raw_input(
self,

View File

@@ -1,12 +1,10 @@
from io import BytesIO
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.http.response import HttpResponse
from django.core.handlers import base
from django.http import HttpRequest
from django.http.response import HttpResponse
_Stream = Union[BytesIO, str]
_WSGIEnviron = Dict[str, Any]
@@ -22,7 +20,6 @@ class LimitedStream:
class WSGIRequest(HttpRequest):
environ: _WSGIEnviron = ...
user: AbstractUser
session: SessionBase
encoding: Any = ...
def __init__(self, environ: _WSGIEnviron) -> None: ...

View File

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

View File

@@ -8,13 +8,13 @@ class InvalidPage(Exception): ...
class PageNotAnInteger(InvalidPage): ...
class EmptyPage(InvalidPage): ...
class SupportsLen(Protocol):
class _SupportsLen(Protocol):
def __len__(self) -> int: ...
class SupportsCount(Protocol):
class _SupportsCount(Protocol):
def count(self) -> int: ...
class SupportsOrdered(Protocol):
class _SupportsOrdered(Protocol):
ordered: bool = ...
class Paginator:
@@ -24,7 +24,7 @@ class Paginator:
allow_empty_first_page: bool = ...
def __init__(
self,
object_list: Union[SupportsLen, SupportsCount, SupportsOrdered],
object_list: Union[_SupportsLen, _SupportsCount, _SupportsOrdered],
per_page: Union[int, str],
orphans: int = ...,
allow_empty_first_page: bool = ...,

View File

@@ -21,7 +21,11 @@ def dumps(
obj: Any, key: None = ..., salt: str = ..., serializer: Type[Serializer] = ..., compress: bool = ...
) -> str: ...
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: ...
class Signer:

View File

@@ -1,6 +1,6 @@
from decimal import Decimal
from re import RegexFlag
from typing import Any, Dict, List, Optional, Union, Pattern, Collection, Callable, Tuple
from typing import Any, Callable, Collection, Dict, List, Optional, Pattern, Tuple, Union
from django.core.files.base import File
@@ -13,7 +13,7 @@ def _lazy_re_compile(regex: _Regex, flags: int = ...): ...
class RegexValidator:
regex: _Regex = ...
message: Any = ...
message: str = ...
code: str = ...
inverse_match: bool = ...
flags: int = ...
@@ -31,24 +31,24 @@ class URLValidator(RegexValidator):
ul: str = ...
ipv4_re: str = ...
ipv6_re: str = ...
hostname_re: Any = ...
domain_re: Any = ...
tld_re: Any = ...
host_re: Any = ...
schemes: Any = ...
hostname_re: str = ...
domain_re: str = ...
tld_re: str = ...
host_re: str = ...
schemes: List[str] = ...
def __init__(self, schemes: Optional[Collection[str]] = ..., **kwargs: Any) -> None: ...
integer_validator: Any
integer_validator: RegexValidator = ...
def validate_integer(value: Optional[Union[float, str]]) -> None: ...
class EmailValidator:
message: str = ...
code: str = ...
user_regex: Any = ...
domain_regex: Any = ...
literal_regex: Any = ...
domain_whitelist: Any = ...
user_regex: Pattern = ...
domain_regex: Pattern = ...
literal_regex: Pattern = ...
domain_whitelist: List[str] = ...
def __init__(
self,
message: Optional[_ErrorMessage] = ...,
@@ -68,9 +68,10 @@ def validate_ipv4_address(value: str) -> None: ...
def validate_ipv6_address(value: str) -> None: ...
def validate_ipv46_address(value: str) -> None: ...
ip_address_validator_map: Dict[str, Tuple[Callable[[Any], None], str]]
_IPValidator = Tuple[Callable[[Any], None], str]
ip_address_validator_map: Dict[str, _IPValidator]
def ip_address_validators(protocol: str, unpack_ipv4: bool) -> Any: ...
def ip_address_validators(protocol: str, unpack_ipv4: bool) -> _IPValidator: ...
def int_list_validator(
sep: str = ..., message: Optional[_ErrorMessage] = ..., code: str = ..., allow_negative: bool = ...
) -> RegexValidator: ...
@@ -92,7 +93,7 @@ class MinLengthValidator(BaseValidator): ...
class MaxLengthValidator(BaseValidator): ...
class DecimalValidator:
messages: Any = ...
messages: Dict[str, str] = ...
max_digits: int = ...
decimal_places: int = ...
def __init__(self, max_digits: Optional[Union[int, str]], decimal_places: Optional[Union[int, str]]) -> None: ...

View File

@@ -68,6 +68,9 @@ from .deletion import (
DO_NOTHING as DO_NOTHING,
PROTECT as PROTECT,
SET as SET,
RESTRICT as RESTRICT,
ProtectedError as ProtectedError,
RestrictedError as RestrictedError,
)
from .query import (

View File

@@ -7,6 +7,13 @@ from django.db.models.options import Options
_Self = TypeVar("_Self", bound="Model")
class ModelStateFieldsCacheDescriptor: ...
class ModelState:
db: Optional[str] = ...
adding: bool = ...
fields_cache: ModelStateFieldsCacheDescriptor = ...
class ModelBase(type): ...
class Model(metaclass=ModelBase):
@@ -17,7 +24,12 @@ class Model(metaclass=ModelBase):
_default_manager: BaseManager[Model]
objects: BaseManager[Any]
pk: Any = ...
_state: ModelState
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 full_clean(self, exclude: Optional[Collection[str]] = ..., validate_unique: bool = ...) -> None: ...
def clean(self) -> None: ...
@@ -46,10 +58,3 @@ class Model(metaclass=ModelBase):
@classmethod
def check(cls, **kwargs: Any) -> List[CheckMessage]: ...
def __getstate__(self) -> dict: ...
class ModelStateFieldsCacheDescriptor: ...
class ModelState:
db: None = ...
adding: bool = ...
fields_cache: ModelStateFieldsCacheDescriptor = ...

View File

@@ -11,10 +11,12 @@ def SET_NULL(collector, field, sub_objs, using): ...
def SET_DEFAULT(collector, field, sub_objs, using): ...
def DO_NOTHING(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 get_candidate_relations_to_delete(opts: Options) -> Iterable[Field]: ...
class ProtectedError(IntegrityError): ...
class RestrictedError(IntegrityError): ...
class Collector:
def __init__(self, using: str) -> None: ...

View File

@@ -108,7 +108,10 @@ class Field(RegisterLookupMixin, Generic[_ST, _GT]):
def set_attributes_from_name(self, name: str) -> None: ...
def db_type(self, connection: Any) -> 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_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: ...
# TODO: plugin support
def formfield(self, **kwargs) -> Any: ...

View File

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

View File

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

View File

@@ -14,7 +14,7 @@ class Field:
initial: Any
label: Optional[str]
required: bool
widget: Widget = ...
widget: Type[Widget] = ...
hidden_widget: Any = ...
default_validators: Any = ...
default_error_messages: Any = ...

View File

@@ -1,11 +1,13 @@
from typing import Any, Dict, Iterator, List, Mapping, Optional, Sequence, Type, Union
from django.core.exceptions import ValidationError as ValidationError
from django.core.files import uploadedfile
from django.forms.boundfield import BoundField
from django.forms.fields import Field
from django.forms.renderers import BaseRenderer
from django.forms.utils import ErrorDict, ErrorList
from django.forms.widgets import Media, MediaDefiningClass
from django.utils.datastructures import MultiValueDict
from django.utils.safestring import SafeText
class DeclarativeFieldsMetaclass(MediaDefiningClass): ...
@@ -18,11 +20,11 @@ class BaseForm:
use_required_attribute: bool = ...
is_bound: bool = ...
data: Dict[str, Any] = ...
files: Optional[Dict[str, Any]] = ...
auto_id: str = ...
files: MultiValueDict[str, uploadedfile.UploadedFile] = ...
auto_id: Union[bool, str] = ...
initial: Dict[str, Any] = ...
error_class: Type[ErrorList] = ...
prefix: str = ...
prefix: Optional[str] = ...
label_suffix: str = ...
empty_permitted: bool = ...
fields: Dict[str, Any] = ...
@@ -67,6 +69,9 @@ class BaseForm:
def hidden_fields(self): ...
def visible_fields(self): ...
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):
base_fields: Dict[str, Field]

View File

@@ -17,6 +17,7 @@ from typing import (
)
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.sites.models import Site
from django.utils.datastructures import CaseInsensitiveMapping, ImmutableList, MultiValueDict
@@ -51,7 +52,7 @@ class HttpRequest(BytesIO):
resolver_match: ResolverMatch = ...
content_type: Optional[str] = ...
content_params: Optional[Dict[str, str]] = ...
user: AbstractBaseUser
user: Union[AbstractBaseUser, AnonymousUser]
site: Site
session: SessionBase
encoding: Optional[str] = ...

View File

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

View File

@@ -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.http.response import (
@@ -10,9 +11,14 @@ from django.http.response import (
from django.db.models import Manager, QuerySet
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(
template_name: Union[str, Sequence[str]],
context: Optional[Dict[str, Any]] = ...,
context: Optional[Mapping[str, Any]] = ...,
content_type: Optional[str] = ...,
status: Optional[int] = ...,
using: Optional[str] = ...,
@@ -20,7 +26,7 @@ def render_to_response(
def render(
request: HttpRequest,
template_name: Union[str, Sequence[str]],
context: Optional[Dict[str, Any]] = ...,
context: Optional[Mapping[str, Any]] = ...,
content_type: Optional[str] = ...,
status: Optional[int] = ...,
using: Optional[str] = ...,
@@ -28,6 +34,15 @@ def render(
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(
to: Union[Callable, str, SupportsGetAbsoluteUrl], *args: Any, permanent: bool = ..., **kwargs: Any
) -> Union[HttpResponseRedirect, HttpResponsePermanentRedirect]: ...

View File

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

View File

@@ -126,9 +126,17 @@ class SimpleTestCase(unittest.TestCase):
self, needle: str, haystack: SafeText, count: Optional[int] = ..., msg_prefix: str = ...
) -> None: ...
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: ...
def assertJSONNotEqual(self, raw: str, expected_data: 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: ...

View File

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

View File

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

View File

@@ -22,6 +22,7 @@ _V = TypeVar("_V")
class OrderedSet(MutableSet[_K]):
dict: Dict[_K, None] = ...
def __init__(self, iterable: Optional[Iterable[_K]] = ...) -> None: ...
def __contains__(self, item: object) -> bool: ...
def __iter__(self) -> Iterator[_K]: ...
def __len__(self) -> int: ...

View File

@@ -1,13 +1,16 @@
from typing import Any, Callable, Iterable, Optional, Type, Union
from typing import Any, Callable, Iterable, Optional, Type, Union, TypeVar
from django.utils.deprecation import MiddlewareMixin
from django.views.generic.base import View
_T = TypeVar("_T", bound=Union[View, Callable]) # Any callable
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(middleware_class: type) -> Callable: ...
def available_attrs(fn: Any): ...
def available_attrs(fn: Callable): ...
def make_middleware_decorator(middleware_class: Type[MiddlewareMixin]) -> Callable: ...
class classproperty:

View File

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

View File

@@ -13,6 +13,6 @@ NOCOLOR_PALETTE: str
DARK_PALETTE: str
LIGHT_PALETTE: str
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]]]]: ...

View File

@@ -66,6 +66,7 @@ def to_locale(language: str) -> str: ...
def get_language_from_request(request: WSGIRequest, check_path: bool = ...) -> str: ...
def templatize(src: str, **kwargs: Any) -> str: ...
def deactivate_all() -> None: ...
def get_supported_language_variant(lang_code: str, strict: bool = ...) -> str: ...
def get_language_info(lang_code: str) -> Any: ...
from . import trans_real as trans_real

View File

@@ -10,3 +10,5 @@ select = F401, Y
max_line_length = 120
per-file-ignores =
*__init__.pyi: F401
base_user.pyi: Y003
models.pyi: Y003

View File

@@ -10,8 +10,8 @@ from mypy import checker
from mypy.checker import TypeChecker
from mypy.mro import calculate_mro
from mypy.nodes import (
GDEF, MDEF, Argument, Block, ClassDef, Expression, FuncDef, MemberExpr, MypyFile, NameExpr, StrExpr, SymbolNode,
SymbolTable, SymbolTableNode, TypeInfo, Var,
GDEF, MDEF, Argument, Block, ClassDef, Expression, FuncDef, MemberExpr, MypyFile, NameExpr, PlaceholderNode,
StrExpr, SymbolNode, SymbolTable, SymbolTableNode, TypeInfo, Var,
)
from mypy.plugin import (
AttributeContext, CheckerPluginInterface, ClassDefContext, DynamicClassDefContext, FunctionContext, MethodContext,
@@ -309,34 +309,67 @@ def add_new_sym_for_info(info: TypeInfo, *, name: str, sym_type: MypyType) -> No
plugin_generated=True)
def _prepare_new_method_arguments(node: FuncDef) -> Tuple[List[Argument], MypyType]:
arguments = []
for argument in node.arguments[1:]:
if argument.type_annotation is None:
def build_unannotated_method_args(method_node: FuncDef) -> Tuple[List[Argument], MypyType]:
prepared_arguments = []
for argument in method_node.arguments[1:]:
argument.type_annotation = AnyType(TypeOfAny.unannotated)
arguments.append(argument)
if isinstance(node.type, CallableType):
return_type = node.type.ret_type
else:
prepared_arguments.append(argument)
return_type = AnyType(TypeOfAny.unannotated)
return arguments, return_type
return prepared_arguments, return_type
def copy_method_to_another_class(ctx: ClassDefContext, self_type: Instance,
new_method_name: str, method_node: FuncDef) -> None:
arguments, return_type = _prepare_new_method_arguments(method_node)
semanal_api = get_semanal_api(ctx)
for argument in arguments:
if argument.type_annotation is not None:
argument.type_annotation = semanal_api.anal_type(argument.type_annotation)
if return_type is not None:
return_type = semanal_api.anal_type(return_type) or AnyType(TypeOfAny.unannotated)
if method_node.type is None:
if not semanal_api.final_iteration:
semanal_api.defer()
return
arguments, return_type = build_unannotated_method_args(method_node)
add_method(ctx,
new_method_name,
args=arguments,
return_type=return_type,
self_type=self_type)
return
method_type = method_node.type
if not isinstance(method_type, CallableType):
if not semanal_api.final_iteration:
semanal_api.defer()
return
arguments = []
bound_return_type = semanal_api.anal_type(method_type.ret_type,
allow_placeholder=True)
assert bound_return_type is not None
if isinstance(bound_return_type, PlaceholderNode):
return
for arg_name, arg_type, original_argument in zip(method_type.arg_names[1:],
method_type.arg_types[1:],
method_node.arguments[1:]):
bound_arg_type = semanal_api.anal_type(arg_type, allow_placeholder=True)
assert bound_arg_type is not None
if isinstance(bound_arg_type, PlaceholderNode):
return
var = Var(name=original_argument.variable.name,
type=arg_type)
var.line = original_argument.variable.line
var.column = original_argument.variable.column
argument = Argument(variable=var,
type_annotation=bound_arg_type,
initializer=original_argument.initializer,
kind=original_argument.kind)
argument.set_line(original_argument)
arguments.append(argument)
add_method(ctx,
new_method_name,
args=arguments,
return_type=bound_return_type,
self_type=self_type)

View File

@@ -1,6 +1,7 @@
from mypy.plugin import AttributeContext
from mypy.types import Instance
from mypy.types import Type as MypyType
from mypy.types import UnionType
from mypy_django_plugin.django.context import DjangoContext
from mypy_django_plugin.lib import helpers
@@ -8,9 +9,18 @@ from mypy_django_plugin.lib import helpers
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
model_cls = django_context.apps_registry.get_model(auth_user_model)
model_info = helpers.lookup_class_typeinfo(helpers.get_typechecker_api(ctx), model_cls)
if model_info is None:
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 Instance(model_info, [])
# Imported here because django isn't properly loaded yet when module is loaded
from django.contrib.auth.models import AnonymousUser
anonymous_user_info = helpers.lookup_class_typeinfo(helpers.get_typechecker_api(ctx), AnonymousUser)
if anonymous_user_info is None:
# This shouldn't be able to happen, as we managed to import the model above...
return Instance(user_info, [])
return UnionType([Instance(user_info, []), Instance(anonymous_user_info, [])])

View File

@@ -64,7 +64,12 @@ IGNORED_ERRORS = {
'Incompatible types in string interpolation',
'"None" has no attribute',
'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': [
'"Article" has no attribute "non_field"',
@@ -104,6 +109,7 @@ IGNORED_ERRORS = {
],
'builtin_server': [
'"ServerHandler" has no attribute',
'Incompatible types in assignment (expression has type "Tuple[BytesIO, BytesIO]"',
],
'bulk_create': [
'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")'
],
'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': [
'has incompatible type "str"'
@@ -178,7 +185,8 @@ IGNORED_ERRORS = {
],
'files': [
'Incompatible types in assignment (expression has type "IOBase", variable has type "File")',
'Argument 1 to "write" of "SpooledTemporaryFile"',
'Argument 1 to "TextIOWrapper" has incompatible type "File"; expected "BinaryIO"',
'Incompatible types in assignment (expression has type "BinaryIO", variable has type "File")',
],
'filtered_relation': [
'has no attribute "name"',
@@ -192,7 +200,7 @@ IGNORED_ERRORS = {
],
'from_db_value': [
'"Cash" has no attribute',
'Argument 1 to "__str__" of "Decimal"',
'"__str__" of "Decimal"',
],
'get_object_or_404': [
'Argument 1 to "get_object_or_404" has incompatible type "str"; '
@@ -231,13 +239,8 @@ IGNORED_ERRORS = {
],
'mail': [
'List item 1 has incompatible type "None"; expected "str"',
'Argument 1 to "push" of "SMTPChannel" has incompatible type "str"; expected "bytes"',
'Value of type "Union[List[Message], str, bytes, None]" is not indexable',
'Incompatible types in assignment '
+ '(expression has type "bool", variable has type "Union[SMTP_SSL, SMTP, None]")',
re.compile(
r'Item "(int|str)" of "Union\[Message, str, int, Any\]" has no attribute "(get_content_type|get_filename)"'
)
],
'messages_tests': [
'List item 0 has incompatible type "Dict[str, Message]"; expected "Message"',
@@ -246,6 +249,7 @@ IGNORED_ERRORS = {
],
'middleware': [
re.compile(r'"(HttpRequest|WSGIRequest)" has no attribute'),
'Incompatible types in assignment (expression has type "HttpResponseBase", variable has type "HttpResponse")',
],
'many_to_many': [
'(expression has type "List[Article]", variable has type "Article_RelatedManager2',
@@ -282,6 +286,7 @@ IGNORED_ERRORS = {
'"ImageFile" has no attribute "was_opened"',
'Incompatible type for "size" of "FloatModel" (got "object", expected "Union[float, int, str, Combinable]")',
'Incompatible type for "value" of "IntegerModel" (got "object", expected',
'"Child" has no attribute "get_foo_display"',
],
'model_forms': [
'"render" of "Widget"',
@@ -320,9 +325,6 @@ IGNORED_ERRORS = {
'model_enums': [
"'bool' is not a valid base class",
],
'multiple_database': [
'Unexpected attribute "extra_arg" for model "Book"'
],
'null_queries': [
"Cannot resolve keyword 'foo' into field"
],
@@ -378,6 +380,7 @@ IGNORED_ERRORS = {
'responses': [
'Argument 1 to "TextIOWrapper" has incompatible type "HttpResponse"; expected "IO[bytes]"',
'"FileLike" has no attribute "closed"',
'Argument 1 to "TextIOWrapper" has incompatible type "HttpResponse"; expected "BinaryIO"',
],
'reverse_lookup': [
"Cannot resolve keyword 'choice' into field"
@@ -385,6 +388,9 @@ IGNORED_ERRORS = {
'settings_tests': [
'Argument 1 to "Settings" has incompatible type "Optional[str]"; expected "str"'
],
'shortcuts': [
'error: "Context" has no attribute "request"',
],
'signals': [
'Argument 1 to "append" of "list" has incompatible type "Tuple[Any, Any, Optional[Any], Any]";'
],
@@ -396,7 +402,7 @@ IGNORED_ERRORS = {
'"RequestSite" of "Union[Site, RequestSite]" has no attribute "id"',
],
'syndication_tests': [
'List or tuple expected as variable arguments'
'Argument 1 to "add_domain" has incompatible type "*Tuple[object, ...]"',
],
'sessions_tests': [
'Incompatible types in assignment (expression has type "None", variable has type "int")',
@@ -437,6 +443,7 @@ IGNORED_ERRORS = {
'test_client_regress': [
'(expression has type "Dict[<nothing>, <nothing>]", variable has type "SessionBase")',
'Unsupported left operand type for + ("None")',
'Argument 1 to "len" has incompatible type "Context"; expected "Sized"',
],
'transactions': [
'Incompatible types in assignment (expression has type "Thread", variable has type "Callable[[], Any]")'
@@ -466,7 +473,7 @@ IGNORED_ERRORS = {
'No overload variant of "join" matches argument types "str", "None"',
'Argument 1 to "Archive" has incompatible type "None"; expected "str"',
'Argument 1 to "to_path" has incompatible type "int"; expected "Union[Path, str]"',
'Cannot infer type argument 1 of "cached_property"',
],
'view_tests': [
"Module 'django.views.debug' has no attribute 'Path'",

View File

@@ -1,5 +1,5 @@
from pytest_mypy.collect import File
from pytest_mypy.item import YamlTestItem
from pytest_mypy_plugins.collect import File
from pytest_mypy_plugins.item import YamlTestItem
def django_plugin_hook(test_item: YamlTestItem) -> None:

View File

@@ -14,8 +14,8 @@ from scripts.enabled_test_modules import (
)
DJANGO_COMMIT_REFS: Dict[str, Tuple[str, str]] = {
'2.2': ('stable/2.2.x', '86befcc172c23170a720b3e0c06db51a99b3da59'),
'3.0': ('stable/3.0.x', '6cb30414bc0f83b49afc4cae76d4af5656effe9a')
'2.2': ('stable/2.2.x', '996be04c3ceb456754d9d527d4d708f30727f07e'),
'3.0': ('stable/3.0.x', 'd9f1792c7649e9f946f4a3a35a76bddf5a412b8b')
}
PROJECT_DIRECTORY = Path(__file__).parent.parent
DJANGO_SOURCE_DIRECTORY = PROJECT_DIRECTORY / 'django-sources' # type: Path

View File

@@ -21,14 +21,14 @@ with open('README.md', 'r') as f:
readme = f.read()
dependencies = [
'mypy>=0.750,<0.760',
'mypy>=0.780,<0.790',
'typing-extensions',
'django',
]
setup(
name="django-stubs",
version="1.3.3",
version="1.5.0",
description='Mypy stubs for Django',
long_description=readme,
long_description_content_type='text/markdown',
@@ -45,6 +45,10 @@ setup(
'Development Status :: 3 - Alpha',
'License :: OSI Approved :: MIT License',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7'
]
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8'
],
project_urls={
'Release notes': 'https://github.com/typeddjango/django-stubs/releases',
},
)

View File

@@ -0,0 +1,126 @@
# "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,)
actions_on_top = True
actions_on_bottom = False
actions_selection_counter = True
admin_site = AdminSite()
# 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 "Callable[[ModelAdmin, HttpRequest, QuerySet[Any]], None]"

View File

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

View File

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

View File

@@ -146,3 +146,36 @@
return param
NewManager = models.Manager.from_queryset(ModelQuerySet)
- case: from_queryset_with_inherited_manager_and_typing_no_return
disable_cache: true
main: |
from myapp.models import MyModel
reveal_type(MyModel().objects) # N: Revealed type is 'myapp.models.MyModel_NewManager[myapp.models.MyModel]'
reveal_type(MyModel().objects.get()) # N: Revealed type is 'myapp.models.MyModel*'
reveal_type(MyModel().objects.base_queryset_method) # N: Revealed type is 'def (param: Union[builtins.int, builtins.str]) -> <nothing>'
reveal_type(MyModel().objects.base_queryset_method(2)) # N: Revealed type is '<nothing>'
installed_apps:
- myapp
files:
- path: myapp/__init__.py
- path: myapp/models.py
content: |
from django.db import models
from myapp.managers import NewManager
class MyModel(models.Model):
objects = NewManager()
- path: myapp/managers.py
content: |
from django.db import models
from myapp.base_queryset import BaseQuerySet
class ModelQuerySet(BaseQuerySet):
pass
NewManager = models.Manager.from_queryset(ModelQuerySet)
- path: myapp/base_queryset.py
content: |
from typing import NoReturn, Union
from django.db import models
class BaseQuerySet(models.QuerySet):
def base_queryset_method(self, param: Union[int, str]) -> NoReturn:
raise ValueError

View File

@@ -307,15 +307,15 @@
- case: custom_manager_returns_proper_model_types
main: |
from myapp.models import User
reveal_type(User.objects) # N: Revealed type is 'myapp.models.User_MyManager[myapp.models.User]'
reveal_type(User.objects.select_related()) # N: Revealed type is 'myapp.models.User_MyManager[myapp.models.User]'
reveal_type(User.objects) # N: Revealed type is 'myapp.models.User_MyManager2[myapp.models.User]'
reveal_type(User.objects.select_related()) # N: Revealed type is 'myapp.models.User_MyManager2[myapp.models.User]'
reveal_type(User.objects.get()) # N: Revealed type is 'myapp.models.User*'
reveal_type(User.objects.get_instance()) # N: Revealed type is 'builtins.int'
reveal_type(User.objects.get_instance_untyped('hello')) # N: Revealed type is 'Any'
from myapp.models import ChildUser
reveal_type(ChildUser.objects) # N: Revealed type is 'myapp.models.ChildUser_MyManager[myapp.models.ChildUser]'
reveal_type(ChildUser.objects.select_related()) # N: Revealed type is 'myapp.models.ChildUser_MyManager[myapp.models.ChildUser]'
reveal_type(ChildUser.objects) # N: Revealed type is 'myapp.models.ChildUser_MyManager2[myapp.models.ChildUser]'
reveal_type(ChildUser.objects.select_related()) # N: Revealed type is 'myapp.models.ChildUser_MyManager2[myapp.models.ChildUser]'
reveal_type(ChildUser.objects.get()) # N: Revealed type is 'myapp.models.ChildUser*'
reveal_type(ChildUser.objects.get_instance()) # N: Revealed type is 'builtins.int'
reveal_type(ChildUser.objects.get_instance_untyped('hello')) # N: Revealed type is 'Any'

View File

@@ -10,6 +10,12 @@
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().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
reveal_type(Permission().name) # N: Revealed type is 'builtins.str*'

View 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

View File

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

View File

@@ -2,11 +2,11 @@
disable_cache: true
main: |
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
reveal_type(HttpRequest().method) # N: Revealed type is 'Union[builtins.str, None]'
custom_settings: |
INSTALLED_APPS = ('django.contrib.contenttypes', 'myapp')
INSTALLED_APPS = ('django.contrib.contenttypes', 'django.contrib.auth', 'myapp')
AUTH_USER_MODEL='myapp.MyUser'
files:
- path: myapp/__init__.py
@@ -15,3 +15,15 @@
from django.db import models
class MyUser(models.Model):
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')

View File

@@ -36,3 +36,24 @@
from django.db import models
class MyUser(models.Model):
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]'

View File

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

View File

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