mirror of
https://github.com/davidhalter/django-stubs.git
synced 2025-12-10 22:11:54 +08:00
Compare commits
57 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1af3a12f2c | ||
|
|
7af89ee6a6 | ||
|
|
afa16bfb74 | ||
|
|
f77073157b | ||
|
|
fe3b95c611 | ||
|
|
d0f9730c53 | ||
|
|
0fdd678d65 | ||
|
|
2397065fa6 | ||
|
|
04023a9f31 | ||
|
|
95e6c94319 | ||
|
|
d96aee7a8b | ||
|
|
2489bb9b04 | ||
|
|
3a8f278c88 | ||
|
|
85b65b4578 | ||
|
|
150e8e862a | ||
|
|
5b3088a17a | ||
|
|
f89a0fbbaa | ||
|
|
438f8b5829 | ||
|
|
836d5acd8f | ||
|
|
bfae51e64c | ||
|
|
e3801918e3 | ||
|
|
6f296b0a91 | ||
|
|
7ba578f6b2 | ||
|
|
cb123de105 | ||
|
|
38135f2d1f | ||
|
|
998b659749 | ||
|
|
72f69e1c5e | ||
|
|
d666ecd36f | ||
|
|
c1af26c027 | ||
|
|
3c3dfcbc9f | ||
|
|
1196336e3b | ||
|
|
665f4d8ea1 | ||
|
|
b3ed9e4827 | ||
|
|
fb1593630a | ||
|
|
031d42a75d | ||
|
|
f7e1cfc6c7 | ||
|
|
d0c25e3bce | ||
|
|
1c31e71ffc | ||
|
|
6b3b6be3c1 | ||
|
|
5832605053 | ||
|
|
31e795016f | ||
|
|
0cba3f9fd6 | ||
|
|
f02050911f | ||
|
|
e8e6fca78c | ||
|
|
ade48b6546 | ||
|
|
b8f29027d8 | ||
|
|
eba3f6cb15 | ||
|
|
5a45544e76 | ||
|
|
8c2de7da56 | ||
|
|
d43c6dc7e2 | ||
|
|
f7e2109e06 | ||
|
|
cea62abf5a | ||
|
|
3b69ec6a72 | ||
|
|
7e794534c0 | ||
|
|
f5f33b061d | ||
|
|
58b26fdbd3 | ||
|
|
9ca79c24a2 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -11,3 +11,4 @@ pip-wheel-metadata/
|
||||
.pytest_cache/
|
||||
/.envrc
|
||||
/.direnv
|
||||
django-sources/
|
||||
@@ -11,19 +11,16 @@ jobs:
|
||||
- name: Typecheck Django 3.0 test suite with python 3.7
|
||||
python: 3.7
|
||||
script: |
|
||||
pip install Django==3.0.*
|
||||
python ./scripts/typecheck_tests.py --django_version=3.0
|
||||
|
||||
- name: Typecheck Django 3.0 test suite with python 3.6
|
||||
python: 3.6
|
||||
script: |
|
||||
pip install Django==3.0.*
|
||||
python ./scripts/typecheck_tests.py --django_version=3.0
|
||||
|
||||
- name: Typecheck Django 2.2 test suite with python 3.7
|
||||
python: 3.7
|
||||
script: |
|
||||
pip install Django==2.2.*
|
||||
python ./scripts/typecheck_tests.py --django_version=2.2
|
||||
|
||||
- name: Mypy for plugin code
|
||||
|
||||
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.
|
||||
103
README.md
103
README.md
@@ -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
|
||||
|
||||
[](https://travis-ci.com/typeddjango/django-stubs)
|
||||
[](http://mypy-lang.org/)
|
||||
[](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,8 +15,35 @@ Could be run on earlier versions of Django, but expect some missing imports warn
|
||||
pip install django-stubs
|
||||
```
|
||||
|
||||
See [Configuration](#configuration) section to get started.
|
||||
|
||||
## Mypy compatibility
|
||||
|
||||
## Configuration
|
||||
|
||||
To make `mypy` happy, you will need to add:
|
||||
|
||||
```ini
|
||||
[mypy]
|
||||
plugins =
|
||||
mypy_django_plugin.main
|
||||
|
||||
[mypy.plugins.django-stubs]
|
||||
django_settings_module = "myproject.settings"
|
||||
```
|
||||
|
||||
in your `mypy.ini` or `setup.cfg` [file](https://mypy.readthedocs.io/en/latest/config_file.html).
|
||||
|
||||
Two things happeining here:
|
||||
|
||||
1. We need to explicitly list our plugin to be loaded by `mypy`
|
||||
2. Our plugin also requires `django` settings module (what you put into `DJANGO_SETTINGS_MODULE` variable) to be specified
|
||||
|
||||
This fully working [typed boilerplate](https://github.com/wemake-services/wemake-django-template) can serve you as an example.
|
||||
|
||||
|
||||
## Version compatibility
|
||||
|
||||
We rely on different `django` and `mypy` versions:
|
||||
|
||||
| django-stubs | mypy version | django version | python version
|
||||
| ------------ | ---- | ---- | ---- |
|
||||
@@ -28,58 +53,66 @@ pip install django-stubs
|
||||
| 0.12.x | old semantic analyzer (<0.711), dmypy support | 2.1.x | ^3.6
|
||||
|
||||
|
||||
## Configuration
|
||||
## FAQ
|
||||
|
||||
To make mypy aware of the plugin, you need to add
|
||||
### Is this an official Django project?
|
||||
|
||||
```ini
|
||||
[mypy]
|
||||
plugins =
|
||||
mypy_django_plugin.main
|
||||
```
|
||||
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.
|
||||
|
||||
in your `mypy.ini` or `setup.cfg` [file](https://mypy.readthedocs.io/en/latest/config_file.html).
|
||||
### Is it safe to use this in production?
|
||||
|
||||
Plugin also requires Django settings module (what you put into `DJANGO_SETTINGS_MODULE` variable) to be specified.
|
||||
Yes, it is! This project does not affect your runtime at all.
|
||||
It only affects `mypy` type checking process.
|
||||
|
||||
```ini
|
||||
[mypy]
|
||||
strict_optional = True
|
||||
But, it does not make any sense to use this project without `mypy`.
|
||||
|
||||
# This one is new:
|
||||
[mypy.plugins.django-stubs]
|
||||
django_settings_module = mysettings
|
||||
```
|
||||
### mypy crashes when I run it with this plugin installed
|
||||
|
||||
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()`.
|
||||
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.
|
||||
|
||||
This fully working [typed boilerplate](https://github.com/wemake-services/wemake-django-template) can serve you as an example.
|
||||
### I cannot use QuerySet or Manager with type annotations
|
||||
|
||||
You can get a `TypeError: 'type' object is not subscriptable`
|
||||
when you will try to use `QuerySet[MyModel]` or `Manager[MyModel]`.
|
||||
|
||||
## Notes
|
||||
This happens because Django classes do not support [`__class_getitem__`](https://www.python.org/dev/peps/pep-0560/#class-getitem) magic method.
|
||||
|
||||
Type implementation monkey-patches Django to add `__class_getitem__` to the `Manager` class.
|
||||
If you would use Python3.7 and do that too in your code, you can make things like
|
||||
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.
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
black
|
||||
pytest-mypy-plugins==1.1.0
|
||||
pytest-mypy-plugins==1.2.0
|
||||
psycopg2
|
||||
flake8==3.7.8
|
||||
flake8==3.7.9
|
||||
flake8-pyi==19.3.0
|
||||
isort==4.3.21
|
||||
gitpython==3.0.5
|
||||
gitpython==3.1.0
|
||||
-e .
|
||||
|
||||
@@ -1,24 +1,22 @@
|
||||
import threading
|
||||
from collections import OrderedDict
|
||||
from typing import Any, Callable, DefaultDict, Dict, Iterable, List, Optional, Tuple, Type, Union
|
||||
from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple, Type, Union
|
||||
|
||||
from django.db.migrations.state import AppConfigStub
|
||||
from django.db.models.base import Model
|
||||
|
||||
from .config import AppConfig
|
||||
|
||||
class Apps:
|
||||
all_models: Dict[str, OrderedDict[str, Type[Model]]] = ...
|
||||
app_configs: OrderedDict[str, AppConfig] = ...
|
||||
all_models: Dict[str, Dict[str, Type[Model]]] = ...
|
||||
app_configs: Dict[str, AppConfig] = ...
|
||||
stored_app_configs: List[Any] = ...
|
||||
apps_ready: bool = ...
|
||||
ready_event: threading.Event = ...
|
||||
loading: bool = ...
|
||||
_pending_operations: DefaultDict[Tuple[str, str], List]
|
||||
_pending_operations: Dict[Tuple[str, str], List]
|
||||
models_ready: bool = ...
|
||||
ready: bool = ...
|
||||
def __init__(self, installed_apps: Optional[Union[List[AppConfigStub], List[str], Tuple]] = ...) -> None: ...
|
||||
def populate(self, installed_apps: Union[List[AppConfigStub], List[str], Tuple] = ...) -> None: ...
|
||||
def __init__(self, installed_apps: Optional[Iterable[Union[AppConfig, str]]] = ...) -> None: ...
|
||||
def populate(self, installed_apps: Iterable[Union[AppConfig, str]] = ...) -> None: ...
|
||||
def check_apps_ready(self) -> None: ...
|
||||
def check_models_ready(self) -> None: ...
|
||||
def get_app_configs(self) -> Iterable[AppConfig]: ...
|
||||
@@ -31,9 +29,9 @@ class Apps:
|
||||
def get_containing_app_config(self, object_name: str) -> Optional[AppConfig]: ...
|
||||
def get_registered_model(self, app_label: str, model_name: str) -> Type[Model]: ...
|
||||
def get_swappable_settings_name(self, to_string: str) -> Optional[str]: ...
|
||||
def set_available_apps(self, available: List[str]) -> None: ...
|
||||
def set_available_apps(self, available: Iterable[str]) -> None: ...
|
||||
def unset_available_apps(self) -> None: ...
|
||||
def set_installed_apps(self, installed: Union[List[str], Tuple[str]]) -> None: ...
|
||||
def set_installed_apps(self, installed: Iterable[str]) -> None: ...
|
||||
def unset_installed_apps(self) -> None: ...
|
||||
def clear_cache(self) -> None: ...
|
||||
def lazy_model_operation(self, function: Callable, *model_keys: Any) -> None: ...
|
||||
|
||||
@@ -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]]
|
||||
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
from typing import Any, List, Union
|
||||
from typing import Any, List, Union, Iterable, Optional
|
||||
|
||||
from django.contrib.admin.options import BaseModelAdmin
|
||||
from django.core.checks.messages import Error
|
||||
|
||||
from django.apps.config import AppConfig
|
||||
|
||||
_CheckError = Union[str, Error]
|
||||
|
||||
def check_admin_app(app_configs: None, **kwargs: Any) -> List[_CheckError]: ...
|
||||
def check_admin_app(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[_CheckError]: ...
|
||||
def check_dependencies(**kwargs: Any) -> List[_CheckError]: ...
|
||||
|
||||
class BaseModelAdminChecks:
|
||||
|
||||
@@ -8,6 +8,8 @@ from django.template.response import TemplateResponse
|
||||
from django.urls.resolvers import URLResolver
|
||||
from django.utils.functional import LazyObject
|
||||
|
||||
from django.apps.config import AppConfig
|
||||
|
||||
all_sites: Any
|
||||
|
||||
class AlreadyRegistered(Exception): ...
|
||||
@@ -28,7 +30,7 @@ class AdminSite:
|
||||
name: str = ...
|
||||
_registry: Dict[Type[Model], ModelAdmin]
|
||||
def __init__(self, name: str = ...) -> None: ...
|
||||
def check(self, app_configs: None) -> List[Any]: ...
|
||||
def check(self, app_configs: Optional[Iterable[AppConfig]]) -> List[Any]: ...
|
||||
def register(
|
||||
self,
|
||||
model_or_iterable: Union[Type[Model], Iterable[Type[Model]]],
|
||||
@@ -37,7 +39,7 @@ class AdminSite:
|
||||
) -> None: ...
|
||||
def unregister(self, model_or_iterable: Union[Type[Model], Iterable[Type[Model]]]) -> None: ...
|
||||
def is_registered(self, model: Type[Model]) -> bool: ...
|
||||
def add_action(self, action: Callable, name: None = ...) -> None: ...
|
||||
def add_action(self, action: Callable, name: Optional[str] = ...) -> None: ...
|
||||
def disable_action(self, name: str) -> None: ...
|
||||
def get_action(self, name: str) -> Callable: ...
|
||||
@property
|
||||
@@ -52,14 +54,20 @@ class AdminSite:
|
||||
@property
|
||||
def urls(self) -> Tuple[List[URLResolver], str, str]: ...
|
||||
def each_context(self, request: Any): ...
|
||||
def password_change(self, request: WSGIRequest, extra_context: Dict[str, str] = ...) -> TemplateResponse: ...
|
||||
def password_change_done(self, request: WSGIRequest, extra_context: None = ...) -> TemplateResponse: ...
|
||||
def password_change(
|
||||
self, request: WSGIRequest, extra_context: Optional[Dict[str, Any]] = ...
|
||||
) -> TemplateResponse: ...
|
||||
def password_change_done(
|
||||
self, request: WSGIRequest, extra_context: Optional[Dict[str, Any]] = ...
|
||||
) -> TemplateResponse: ...
|
||||
def i18n_javascript(self, request: WSGIRequest, extra_context: Optional[Dict[Any, Any]] = ...) -> HttpResponse: ...
|
||||
def logout(self, request: WSGIRequest, extra_context: None = ...) -> TemplateResponse: ...
|
||||
def login(self, request: WSGIRequest, extra_context: None = ...) -> HttpResponse: ...
|
||||
def logout(self, request: WSGIRequest, extra_context: Optional[Dict[str, Any]] = ...) -> TemplateResponse: ...
|
||||
def login(self, request: WSGIRequest, extra_context: Optional[Dict[str, Any]] = ...) -> HttpResponse: ...
|
||||
def get_app_list(self, request: WSGIRequest) -> List[Any]: ...
|
||||
def index(self, request: WSGIRequest, extra_context: Optional[Dict[str, str]] = ...) -> TemplateResponse: ...
|
||||
def app_index(self, request: WSGIRequest, app_label: str, extra_context: None = ...) -> TemplateResponse: ...
|
||||
def index(self, request: WSGIRequest, extra_context: Optional[Dict[str, Any]] = ...) -> TemplateResponse: ...
|
||||
def app_index(
|
||||
self, request: WSGIRequest, app_label: str, extra_context: Optional[Dict[str, Any]] = ...
|
||||
) -> TemplateResponse: ...
|
||||
|
||||
class DefaultAdminSite(LazyObject): ...
|
||||
|
||||
|
||||
@@ -9,12 +9,10 @@ from django.contrib.auth.forms import AdminPasswordChangeForm
|
||||
from django.core.handlers.wsgi import WSGIRequest
|
||||
from django.db.models.base import Model
|
||||
from django.db.models.deletion import Collector
|
||||
from django.db.models.fields.mixins import FieldCacheMixin
|
||||
from django.db.models.fields.reverse_related import ManyToOneRel
|
||||
from django.db.models.options import Options
|
||||
from django.db.models.query import QuerySet
|
||||
from django.forms.forms import BaseForm
|
||||
from django.utils.safestring import SafeText
|
||||
|
||||
from django.db.models.fields import Field, reverse_related
|
||||
|
||||
@@ -27,7 +25,7 @@ def unquote(s: str) -> str: ...
|
||||
def flatten(fields: Any) -> List[Union[Callable, str]]: ...
|
||||
def flatten_fieldsets(fieldsets: Any) -> List[Union[Callable, str]]: ...
|
||||
def get_deleted_objects(
|
||||
objs: QuerySet, request: WSGIRequest, admin_site: AdminSite
|
||||
objs: Sequence[Optional[Model]], request: WSGIRequest, admin_site: AdminSite
|
||||
) -> Tuple[List[Any], Dict[Any, Any], Set[Any], List[Any]]: ...
|
||||
|
||||
class NestedObjects(Collector):
|
||||
@@ -41,22 +39,14 @@ class NestedObjects(Collector):
|
||||
model_objs: Any = ...
|
||||
def __init__(self, *args: Any, **kwargs: Any) -> None: ...
|
||||
def add_edge(self, source: Optional[Model], target: Model) -> None: ...
|
||||
def collect(
|
||||
self,
|
||||
objs: Union[Sequence[Optional[Model]], QuerySet],
|
||||
source: Optional[Type[Model]] = ...,
|
||||
source_attr: Optional[str] = ...,
|
||||
**kwargs: Any
|
||||
) -> None: ...
|
||||
def related_objects(self, related: ManyToOneRel, objs: List[Model]) -> QuerySet: ...
|
||||
def nested(self, format_callback: Callable = ...) -> Union[List[SafeText], List[int]]: ...
|
||||
def can_fast_delete(self, *args: Any, **kwargs: Any) -> bool: ...
|
||||
def related_objects(self, related: ManyToOneRel, objs: Sequence[Optional[Model]]) -> QuerySet: ...
|
||||
def nested(self, format_callback: Callable = ...) -> List[Any]: ...
|
||||
|
||||
def model_format_dict(obj: Any): ...
|
||||
def model_ngettext(obj: Union[Options, QuerySet], n: Optional[int] = ...) -> str: ...
|
||||
def lookup_field(
|
||||
name: Union[Callable, str], obj: Model, model_admin: BaseModelAdmin = ...
|
||||
) -> Tuple[Optional[Field], Callable, Callable]: ...
|
||||
) -> Tuple[Optional[Field], Any, Any]: ...
|
||||
def label_for_field(
|
||||
name: Union[Callable, str],
|
||||
model: Type[Model],
|
||||
@@ -65,16 +55,14 @@ def label_for_field(
|
||||
form: Optional[BaseForm] = ...,
|
||||
) -> Union[Tuple[Optional[str], Union[Callable, Type[str]]], str]: ...
|
||||
def help_text_for_field(name: str, model: Type[Model]) -> str: ...
|
||||
def display_for_field(
|
||||
value: Any, field: Union[Field, reverse_related.OneToOneRel], empty_value_display: str
|
||||
) -> str: ...
|
||||
def display_for_field(value: Any, field: Field, empty_value_display: str) -> str: ...
|
||||
def display_for_value(value: Any, empty_value_display: str, boolean: bool = ...) -> str: ...
|
||||
|
||||
class NotRelationField(Exception): ...
|
||||
|
||||
def get_model_from_relation(field: Union[Field, reverse_related.ForeignObjectRel]) -> Type[Model]: ...
|
||||
def reverse_field_path(model: Type[Model], path: str) -> Tuple[Type[Model], str]: ...
|
||||
def get_fields_from_path(model: Type[Model], path: str) -> List[Union[Field, FieldCacheMixin]]: ...
|
||||
def get_fields_from_path(model: Type[Model], path: str) -> List[Field]: ...
|
||||
def construct_change_message(
|
||||
form: AdminPasswordChangeForm, formsets: None, add: bool
|
||||
) -> List[Dict[str, Dict[str, List[str]]]]: ...
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from typing import Any, Optional, Set, Union
|
||||
|
||||
from django.contrib.auth.base_user import AbstractBaseUser
|
||||
from django.contrib.auth.models import AnonymousUser, User
|
||||
from django.contrib.auth.models import AnonymousUser, User, Permission
|
||||
|
||||
from django.db.models.base import Model
|
||||
|
||||
@@ -9,17 +9,26 @@ _AnyUser = Union[Model, AnonymousUser]
|
||||
|
||||
UserModel: Any
|
||||
|
||||
class ModelBackend:
|
||||
class BaseBackend:
|
||||
def authenticate(
|
||||
self, request: Any, username: Optional[str] = ..., password: Optional[str] = ..., **kwargs: Any
|
||||
) -> Optional[AbstractBaseUser]: ...
|
||||
def user_can_authenticate(self, user: Optional[_AnyUser]) -> bool: ...
|
||||
def get_user(self, user_id: int) -> Optional[AbstractBaseUser]: ...
|
||||
def get_user_permissions(self, user_obj: _AnyUser, obj: Optional[Model] = ...) -> Set[str]: ...
|
||||
def get_group_permissions(self, user_obj: _AnyUser, obj: Optional[Model] = ...) -> Set[str]: ...
|
||||
def get_all_permissions(self, user_obj: _AnyUser, obj: Optional[Model] = ...) -> Set[str]: ...
|
||||
def has_perm(self, user_obj: _AnyUser, perm: str, obj: Optional[Model] = ...) -> bool: ...
|
||||
|
||||
class ModelBackend(BaseBackend):
|
||||
def has_module_perms(self, user_obj: _AnyUser, app_label: str) -> bool: ...
|
||||
def get_user(self, user_id: int) -> AbstractBaseUser: ...
|
||||
def user_can_authenticate(self, user: Optional[_AnyUser]) -> bool: ...
|
||||
def with_perm(
|
||||
self,
|
||||
perm: Union[str, Permission],
|
||||
is_active: bool = ...,
|
||||
include_superusers: bool = ...,
|
||||
obj: Optional[Model] = ...,
|
||||
): ...
|
||||
|
||||
class AllowAllUsersModelBackend(ModelBackend): ...
|
||||
|
||||
|
||||
@@ -13,11 +13,10 @@ class BaseUserManager(models.Manager[_T]):
|
||||
def get_by_natural_key(self, username: Optional[str]) -> _T: ...
|
||||
|
||||
class AbstractBaseUser(models.Model):
|
||||
REQUIRED_FIELDS: List[str] = ...
|
||||
|
||||
password = models.CharField(max_length=128)
|
||||
last_login = models.DateTimeField(blank=True, null=True)
|
||||
|
||||
REQUIRED_FIELDS: List[str] = ...
|
||||
class Meta: ...
|
||||
def get_username(self) -> str: ...
|
||||
def natural_key(self) -> Tuple[str]: ...
|
||||
@property
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
from typing import Any, List
|
||||
from typing import Any, List, Iterable, Optional
|
||||
|
||||
from django.core.checks.messages import CheckMessage
|
||||
|
||||
def check_user_model(app_configs: None = ..., **kwargs: Any) -> List[CheckMessage]: ...
|
||||
def check_models_permissions(app_configs: None = ..., **kwargs: Any) -> List[Any]: ...
|
||||
from django.apps.config import AppConfig
|
||||
|
||||
def check_user_model(app_configs: Optional[Iterable[AppConfig]] = ..., **kwargs: Any) -> List[CheckMessage]: ...
|
||||
def check_models_permissions(app_configs: Optional[Iterable[AppConfig]] = ..., **kwargs: Any) -> List[Any]: ...
|
||||
|
||||
@@ -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]: ...
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from typing import Any, Collection, Optional, Set, Tuple, Type, TypeVar, Union
|
||||
|
||||
from django.contrib.auth.backends import ModelBackend
|
||||
from django.contrib.auth.base_user import AbstractBaseUser as AbstractBaseUser, BaseUserManager as BaseUserManager
|
||||
from django.contrib.auth.validators import UnicodeUsernameValidator
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
@@ -43,11 +44,20 @@ class UserManager(BaseUserManager[_T]):
|
||||
def create_superuser(
|
||||
self, username: str, email: Optional[str], password: Optional[str], **extra_fields: Any
|
||||
) -> _T: ...
|
||||
def with_perm(
|
||||
self,
|
||||
perm: Union[str, Permission],
|
||||
is_active: bool = ...,
|
||||
include_superusers: bool = ...,
|
||||
backend: Optional[Union[Type[ModelBackend], str]] = ...,
|
||||
obj: Optional[Model] = ...,
|
||||
): ...
|
||||
|
||||
class PermissionsMixin(models.Model):
|
||||
is_superuser = models.BooleanField()
|
||||
groups = models.ManyToManyField(Group)
|
||||
user_permissions = models.ManyToManyField(Permission)
|
||||
def get_user_permissions(self, obj: Optional[_AnyUser] = ...) -> Set[str]: ...
|
||||
def get_group_permissions(self, obj: Optional[_AnyUser] = ...) -> Set[str]: ...
|
||||
def get_all_permissions(self, obj: Optional[_AnyUser] = ...) -> Set[str]: ...
|
||||
def has_perm(self, perm: str, obj: Optional[_AnyUser] = ...) -> bool: ...
|
||||
@@ -88,6 +98,7 @@ class AnonymousUser:
|
||||
def groups(self) -> EmptyManager: ...
|
||||
@property
|
||||
def user_permissions(self) -> EmptyManager: ...
|
||||
def get_user_permissions(self, obj: Optional[_AnyUser] = ...) -> Set[str]: ...
|
||||
def get_group_permissions(self, obj: Optional[_AnyUser] = ...) -> Set[Any]: ...
|
||||
def get_all_permissions(self, obj: Optional[_AnyUser] = ...) -> Set[str]: ...
|
||||
def has_perm(self, perm: str, obj: Optional[_AnyUser] = ...) -> bool: ...
|
||||
|
||||
@@ -55,6 +55,7 @@ class PasswordResetDoneView(PasswordContextMixin, TemplateView):
|
||||
class PasswordResetConfirmView(PasswordContextMixin, FormView):
|
||||
post_reset_login: bool = ...
|
||||
post_reset_login_backend: Any = ...
|
||||
reset_url_token: str = ...
|
||||
title: Any = ...
|
||||
token_generator: Any = ...
|
||||
validlink: bool = ...
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
from typing import Any, List
|
||||
from typing import Any, List, Iterable, Optional
|
||||
|
||||
def check_generic_foreign_keys(app_configs: None = ..., **kwargs: Any) -> List[Any]: ...
|
||||
def check_model_name_lengths(app_configs: None = ..., **kwargs: Any) -> List[Any]: ...
|
||||
from django.apps.config import AppConfig
|
||||
|
||||
def check_generic_foreign_keys(app_configs: Optional[Iterable[AppConfig]] = ..., **kwargs: Any) -> List[Any]: ...
|
||||
def check_model_name_lengths(app_configs: Optional[Iterable[AppConfig]] = ..., **kwargs: Any) -> List[Any]: ...
|
||||
|
||||
@@ -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: ...
|
||||
|
||||
18
django-stubs/contrib/postgres/constraints.pyi
Normal file
18
django-stubs/contrib/postgres/constraints.pyi
Normal file
@@ -0,0 +1,18 @@
|
||||
from typing import Optional, Sequence, Tuple, Union
|
||||
|
||||
from django.db.models.constraints import BaseConstraint
|
||||
from django.db.models.expressions import Combinable
|
||||
from django.db.models.query_utils import Q
|
||||
|
||||
class ExclusionConstraint(BaseConstraint):
|
||||
expressions: Sequence[Tuple[Union[str, Combinable], str]]
|
||||
index_type: str
|
||||
condition: Optional[Q]
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
name: str,
|
||||
expressions: Sequence[Tuple[Union[str, Combinable], str]],
|
||||
condition: Optional[Q] = ...,
|
||||
index_type: Optional[str] = ...,
|
||||
): ...
|
||||
@@ -8,6 +8,8 @@ from .ranges import (
|
||||
FloatRangeField as FloatRangeField,
|
||||
DateRangeField as DateRangeField,
|
||||
DateTimeRangeField as DateTimeRangeField,
|
||||
RangeOperators as RangeOperators,
|
||||
RangeBoundary as RangeBoundary,
|
||||
)
|
||||
from .hstore import HStoreField as HStoreField
|
||||
from .citext import (
|
||||
|
||||
@@ -29,3 +29,20 @@ class DateTimeRangeField(RangeField):
|
||||
|
||||
class DateRangeField(RangeField):
|
||||
def __get__(self, instance, owner) -> DateRange: ...
|
||||
|
||||
class RangeOperators:
|
||||
EQUAL: str
|
||||
NOT_EQUAL: str
|
||||
CONTAINS: str
|
||||
CONTAINED_BY: str
|
||||
OVERLAPS: str
|
||||
FULLY_LT: str
|
||||
FULLY_GT: str
|
||||
NOT_LT: str
|
||||
NOT_GT: str
|
||||
ADJACENT_TO: str
|
||||
|
||||
class RangeBoundary(models.Expression):
|
||||
lower: str
|
||||
upper: str
|
||||
def __init__(self, inclusive_lower: bool = ..., inclusive_upper: bool = ...): ...
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
from typing import Any
|
||||
|
||||
from django.apps.config import AppConfig
|
||||
from django.apps.registry import Apps
|
||||
from django.contrib.sites.apps import SitesConfig
|
||||
|
||||
def create_default_site(
|
||||
app_config: SitesConfig,
|
||||
app_config: AppConfig,
|
||||
verbosity: int = ...,
|
||||
interactive: bool = ...,
|
||||
using: str = ...,
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
from typing import Any, List
|
||||
from typing import Any, List, Iterable, Optional
|
||||
|
||||
from django.core.checks.messages import Error
|
||||
|
||||
def check_finders(app_configs: None = ..., **kwargs: Any) -> List[Error]: ...
|
||||
from django.apps.config import AppConfig
|
||||
|
||||
def check_finders(app_configs: Optional[Iterable[AppConfig]] = ..., **kwargs: Any) -> List[Error]: ...
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
from typing import Any, List
|
||||
from typing import Any, List, Iterable, Optional
|
||||
|
||||
from django.core.checks.messages import Error
|
||||
|
||||
from django.apps.config import AppConfig
|
||||
|
||||
E001: Any
|
||||
|
||||
def check_default_cache_is_configured(app_configs: None, **kwargs: Any) -> List[Error]: ...
|
||||
def check_default_cache_is_configured(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Error]: ...
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
from typing import Any, List
|
||||
from typing import Any, List, Iterable, Optional
|
||||
|
||||
from django.core.checks.messages import Warning
|
||||
|
||||
def check_all_models(app_configs: None = ..., **kwargs: Any) -> List[Warning]: ...
|
||||
def check_lazy_references(app_configs: None = ..., **kwargs: Any) -> List[Any]: ...
|
||||
from django.apps.config import AppConfig
|
||||
|
||||
def check_all_models(app_configs: Optional[Iterable[AppConfig]] = ..., **kwargs: Any) -> List[Warning]: ...
|
||||
def check_lazy_references(app_configs: Optional[Iterable[AppConfig]] = ..., **kwargs: Any) -> List[Any]: ...
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
from typing import Any, List
|
||||
from typing import Any, List, Iterable, Optional
|
||||
|
||||
from django.core.checks.messages import Warning
|
||||
|
||||
from django.apps.config import AppConfig
|
||||
|
||||
SECRET_KEY_MIN_LENGTH: int
|
||||
SECRET_KEY_MIN_UNIQUE_CHARACTERS: int
|
||||
W001: Any
|
||||
@@ -17,15 +19,15 @@ W019: Any
|
||||
W020: Any
|
||||
W021: Any
|
||||
|
||||
def check_security_middleware(app_configs: None, **kwargs: Any) -> List[Warning]: ...
|
||||
def check_xframe_options_middleware(app_configs: None, **kwargs: Any) -> List[Warning]: ...
|
||||
def check_sts(app_configs: None, **kwargs: Any) -> List[Warning]: ...
|
||||
def check_sts_include_subdomains(app_configs: None, **kwargs: Any) -> List[Warning]: ...
|
||||
def check_sts_preload(app_configs: None, **kwargs: Any) -> List[Warning]: ...
|
||||
def check_content_type_nosniff(app_configs: None, **kwargs: Any) -> List[Warning]: ...
|
||||
def check_xss_filter(app_configs: None, **kwargs: Any) -> List[Warning]: ...
|
||||
def check_ssl_redirect(app_configs: None, **kwargs: Any) -> List[Warning]: ...
|
||||
def check_secret_key(app_configs: None, **kwargs: Any) -> List[Warning]: ...
|
||||
def check_debug(app_configs: None, **kwargs: Any) -> List[Warning]: ...
|
||||
def check_xframe_deny(app_configs: None, **kwargs: Any) -> List[Warning]: ...
|
||||
def check_allowed_hosts(app_configs: None, **kwargs: Any) -> List[Warning]: ...
|
||||
def check_security_middleware(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Warning]: ...
|
||||
def check_xframe_options_middleware(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Warning]: ...
|
||||
def check_sts(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Warning]: ...
|
||||
def check_sts_include_subdomains(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Warning]: ...
|
||||
def check_sts_preload(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Warning]: ...
|
||||
def check_content_type_nosniff(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Warning]: ...
|
||||
def check_xss_filter(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Warning]: ...
|
||||
def check_ssl_redirect(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Warning]: ...
|
||||
def check_secret_key(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Warning]: ...
|
||||
def check_debug(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Warning]: ...
|
||||
def check_xframe_deny(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Warning]: ...
|
||||
def check_allowed_hosts(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Warning]: ...
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
from typing import Any, List
|
||||
from typing import Any, List, Iterable, Optional
|
||||
|
||||
from django.core.checks.messages import Warning
|
||||
|
||||
from django.apps.config import AppConfig
|
||||
|
||||
W003: Any
|
||||
W016: Any
|
||||
|
||||
def check_csrf_middleware(app_configs: None, **kwargs: Any) -> List[Warning]: ...
|
||||
def check_csrf_cookie_secure(app_configs: None, **kwargs: Any) -> List[Warning]: ...
|
||||
def check_csrf_middleware(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Warning]: ...
|
||||
def check_csrf_cookie_secure(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Warning]: ...
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
from typing import Any, List
|
||||
from typing import Any, List, Iterable, Optional
|
||||
|
||||
from django.core.checks.messages import Warning
|
||||
|
||||
from django.apps.config import AppConfig
|
||||
|
||||
def add_session_cookie_message(message: Any): ...
|
||||
|
||||
W010: Any
|
||||
@@ -14,5 +16,5 @@ W013: Any
|
||||
W014: Any
|
||||
W015: Any
|
||||
|
||||
def check_session_cookie_secure(app_configs: None, **kwargs: Any) -> List[Warning]: ...
|
||||
def check_session_cookie_httponly(app_configs: None, **kwargs: Any) -> List[Warning]: ...
|
||||
def check_session_cookie_secure(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Warning]: ...
|
||||
def check_session_cookie_httponly(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Warning]: ...
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
from typing import Any, List
|
||||
from typing import Any, List, Iterable, Optional
|
||||
|
||||
from django.core.checks.messages import Error
|
||||
|
||||
from django.apps.config import AppConfig
|
||||
|
||||
E001: Any
|
||||
E002: Any
|
||||
|
||||
def check_setting_app_dirs_loaders(app_configs: None, **kwargs: Any) -> List[Error]: ...
|
||||
def check_string_if_invalid_is_string(app_configs: None, **kwargs: Any) -> List[Error]: ...
|
||||
def check_setting_app_dirs_loaders(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Error]: ...
|
||||
def check_string_if_invalid_is_string(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Error]: ...
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
from typing import Any, Callable, List, Tuple, Union
|
||||
from typing import Any, Callable, List, Tuple, Union, Iterable, Optional
|
||||
|
||||
from django.core.checks.messages import CheckMessage, Error, Warning
|
||||
from django.urls.resolvers import URLPattern, URLResolver
|
||||
|
||||
def check_url_config(app_configs: None, **kwargs: Any) -> List[CheckMessage]: ...
|
||||
from django.apps.config import AppConfig
|
||||
|
||||
def check_url_config(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[CheckMessage]: ...
|
||||
def check_resolver(resolver: Union[Tuple[str, Callable], URLPattern, URLResolver]) -> List[CheckMessage]: ...
|
||||
def check_url_namespaces_unique(app_configs: None, **kwargs: Any) -> List[Warning]: ...
|
||||
def check_url_namespaces_unique(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Warning]: ...
|
||||
def get_warning_for_invalid_pattern(pattern: Any) -> List[Error]: ...
|
||||
def check_url_settings(app_configs: None, **kwargs: Any) -> List[Error]: ...
|
||||
def check_url_settings(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Error]: ...
|
||||
def E006(name: str) -> Error: ...
|
||||
|
||||
@@ -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: ...
|
||||
|
||||
@@ -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 = ...
|
||||
|
||||
@@ -1,11 +1,24 @@
|
||||
def supports_color() -> bool: ...
|
||||
|
||||
class Style:
|
||||
def DEBUG(self, text: str) -> str: ...
|
||||
def INFO(self, text: str) -> str: ...
|
||||
def ERROR(self, text: str) -> str: ...
|
||||
def SUCCESS(self, text: str) -> str: ...
|
||||
def WARNING(self, text: str) -> str: ...
|
||||
def ERROR(self, text: str) -> str: ...
|
||||
def NOTICE(self, text: str) -> str: ...
|
||||
def SQL_FIELD(self, text: str) -> str: ...
|
||||
def SQL_COLTYPE(self, text: str) -> str: ...
|
||||
def SQL_KEYWORD(self, text: str) -> str: ...
|
||||
def SQL_TABLE(self, text: str) -> str: ...
|
||||
def HTTP_INFO(self, text: str) -> str: ...
|
||||
def HTTP_SUCCESS(self, text: str) -> str: ...
|
||||
def HTTP_REDIRECT(self, text: str) -> str: ...
|
||||
def HTTP_NOT_MODIFIED(self, text: str) -> str: ...
|
||||
def HTTP_BAD_REQUEST(self, text: str) -> str: ...
|
||||
def HTTP_NOT_FOUND(self, text: str) -> str: ...
|
||||
def HTTP_SERVER_ERROR(self, text: str) -> str: ...
|
||||
def MIGRATE_HEADING(self, text: str) -> str: ...
|
||||
def MIGRATE_LABEL(self, text: str) -> str: ...
|
||||
def ERROR_OUTPUT(self, text: str) -> str: ...
|
||||
|
||||
def make_style(config_string: str = ...) -> Style: ...
|
||||
def no_style() -> Style: ...
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from datetime import date
|
||||
from io import BufferedReader, StringIO, TextIOWrapper
|
||||
from typing import Any, Dict, Iterable, List, Mapping, Optional, Type, Union
|
||||
from typing import Any, Dict, Iterable, List, Mapping, Optional, Type, Union, Collection
|
||||
from uuid import UUID
|
||||
|
||||
from django.core.management.base import OutputWrapper
|
||||
@@ -35,18 +35,18 @@ class Serializer:
|
||||
internal_use_only: bool = ...
|
||||
progress_class: Any = ...
|
||||
stream_class: Any = ...
|
||||
options: Any = ...
|
||||
options: Dict[str, Any] = ...
|
||||
stream: Any = ...
|
||||
selected_fields: Any = ...
|
||||
use_natural_foreign_keys: Any = ...
|
||||
use_natural_primary_keys: Any = ...
|
||||
selected_fields: Optional[Collection[str]] = ...
|
||||
use_natural_foreign_keys: bool = ...
|
||||
use_natural_primary_keys: bool = ...
|
||||
first: bool = ...
|
||||
def serialize(
|
||||
self,
|
||||
queryset: Iterable[Model],
|
||||
*,
|
||||
stream: Optional[Any] = ...,
|
||||
fields: Optional[Any] = ...,
|
||||
fields: Optional[Collection[str]] = ...,
|
||||
use_natural_foreign_keys: bool = ...,
|
||||
use_natural_primary_keys: bool = ...,
|
||||
progress_output: Optional[Any] = ...,
|
||||
@@ -63,7 +63,7 @@ class Serializer:
|
||||
def getvalue(self) -> Optional[Union[bytes, str]]: ...
|
||||
|
||||
class Deserializer:
|
||||
options: Any = ...
|
||||
options: Dict[str, Any] = ...
|
||||
stream: Any = ...
|
||||
def __init__(self, stream_or_string: Union[BufferedReader, TextIOWrapper, str], **options: Any) -> None: ...
|
||||
def __iter__(self) -> Deserializer: ...
|
||||
|
||||
@@ -1,24 +1,10 @@
|
||||
import json
|
||||
from datetime import datetime
|
||||
from decimal import Decimal
|
||||
from io import TextIOWrapper
|
||||
from typing import Any, Union, Dict
|
||||
from uuid import UUID
|
||||
from typing import Any, Dict
|
||||
|
||||
from django.core.serializers.python import Serializer as PythonSerializer
|
||||
from django.db.models.base import Model
|
||||
|
||||
class Serializer(PythonSerializer):
|
||||
json_kwargs: Dict[str, Any]
|
||||
options: Dict[str, None]
|
||||
selected_fields: None
|
||||
stream: TextIOWrapper
|
||||
use_natural_foreign_keys: bool
|
||||
use_natural_primary_keys: bool
|
||||
internal_use_only: bool = ...
|
||||
def start_serialization(self) -> None: ...
|
||||
def end_serialization(self) -> None: ...
|
||||
def end_object(self, obj: Model) -> None: ...
|
||||
|
||||
def Deserializer(stream_or_string: Any, **options: Any) -> None: ...
|
||||
|
||||
@@ -29,4 +15,3 @@ class DjangoJSONEncoder(json.JSONEncoder):
|
||||
indent: int
|
||||
skipkeys: bool
|
||||
sort_keys: bool
|
||||
def default(self, o: Union[datetime, Decimal, UUID]) -> str: ...
|
||||
|
||||
@@ -1,31 +1,15 @@
|
||||
from collections import OrderedDict
|
||||
from io import TextIOWrapper
|
||||
from typing import Any, Dict, Iterator, List
|
||||
from typing import Any, Dict, Iterator, List, Optional
|
||||
|
||||
from django.core.serializers.base import DeserializedObject
|
||||
from django.db.models.base import Model
|
||||
from django.db.models.fields.related import ForeignKey, ManyToManyField
|
||||
|
||||
from django.core.serializers import base
|
||||
from django.db.models.fields import Field
|
||||
|
||||
class Serializer(base.Serializer):
|
||||
options: Dict[Any, Any]
|
||||
selected_fields: None
|
||||
stream: TextIOWrapper
|
||||
use_natural_foreign_keys: bool
|
||||
use_natural_primary_keys: bool
|
||||
internal_use_only: bool = ...
|
||||
objects: List[Any] = ...
|
||||
def start_serialization(self) -> None: ...
|
||||
def end_serialization(self) -> None: ...
|
||||
def start_object(self, obj: Model) -> None: ...
|
||||
def end_object(self, obj: Model) -> None: ...
|
||||
def get_dump_object(self, obj: Model) -> OrderedDict: ...
|
||||
def handle_field(self, obj: Model, field: Field) -> None: ...
|
||||
def handle_fk_field(self, obj: Model, field: ForeignKey) -> None: ...
|
||||
def handle_m2m_field(self, obj: Model, field: ManyToManyField) -> None: ...
|
||||
|
||||
def Deserializer(
|
||||
object_list: List[Dict[str, Any]], *, using: Any = ..., ignorenonexistent: bool = ..., **options: Any
|
||||
object_list: List[Dict[str, Any]], *, using: Optional[str] = ..., ignorenonexistent: bool = ..., **options: Any
|
||||
) -> Iterator[DeserializedObject]: ...
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -1,28 +1,26 @@
|
||||
from datetime import datetime
|
||||
from decimal import Decimal
|
||||
from re import RegexFlag
|
||||
from typing import Any, Dict, List, Optional, Union, Pattern, Collection
|
||||
from uuid import UUID
|
||||
from typing import Any, Callable, Collection, Dict, List, Optional, Pattern, Tuple, Union
|
||||
|
||||
from django.core.files.base import File
|
||||
from django.core.exceptions import ValidationError as ValidationError # noqa: F401
|
||||
|
||||
EMPTY_VALUES: Any
|
||||
|
||||
_Regex = Union[str, Pattern[str]]
|
||||
_ErrorMessage = Union[str, Any]
|
||||
|
||||
def _lazy_re_compile(regex: _Regex, flags: int = ...): ...
|
||||
|
||||
class RegexValidator:
|
||||
regex: _Regex = ...
|
||||
message: Any = ...
|
||||
message: str = ...
|
||||
code: str = ...
|
||||
inverse_match: bool = ...
|
||||
flags: int = ...
|
||||
def __init__(
|
||||
self,
|
||||
regex: Optional[_Regex] = ...,
|
||||
message: Optional[str] = ...,
|
||||
message: Optional[_ErrorMessage] = ...,
|
||||
code: Optional[str] = ...,
|
||||
inverse_match: Optional[bool] = ...,
|
||||
flags: Optional[RegexFlag] = ...,
|
||||
@@ -33,95 +31,82 @@ 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: Any = ...
|
||||
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[str] = ..., code: Optional[str] = ..., whitelist: Optional[Collection[str]] = ...
|
||||
self,
|
||||
message: Optional[_ErrorMessage] = ...,
|
||||
code: Optional[str] = ...,
|
||||
whitelist: Optional[Collection[str]] = ...,
|
||||
) -> None: ...
|
||||
def __call__(self, value: Optional[str]) -> None: ...
|
||||
def validate_domain_part(self, domain_part: str) -> bool: ...
|
||||
|
||||
validate_email: Any
|
||||
slug_re: Any
|
||||
validate_slug: Any
|
||||
slug_unicode_re: Any
|
||||
validate_unicode_slug: Any
|
||||
validate_email: EmailValidator = ...
|
||||
slug_re: Pattern = ...
|
||||
validate_slug: RegexValidator = ...
|
||||
slug_unicode_re: Pattern = ...
|
||||
validate_unicode_slug: RegexValidator = ...
|
||||
|
||||
def validate_ipv4_address(value: str) -> None: ...
|
||||
def validate_ipv6_address(value: str) -> None: ...
|
||||
def validate_ipv46_address(value: str) -> None: ...
|
||||
|
||||
ip_address_validator_map: Any
|
||||
_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: None = ..., code: str = ..., allow_negative: bool = ...
|
||||
sep: str = ..., message: Optional[_ErrorMessage] = ..., code: str = ..., allow_negative: bool = ...
|
||||
) -> RegexValidator: ...
|
||||
|
||||
validate_comma_separated_integer_list: Any
|
||||
|
||||
class BaseValidator:
|
||||
message: Any = ...
|
||||
message: str = ...
|
||||
code: str = ...
|
||||
limit_value: Any = ...
|
||||
def __init__(self, limit_value: Any, message: Optional[str] = ...) -> None: ...
|
||||
def __init__(self, limit_value: Any, message: Optional[_ErrorMessage] = ...) -> None: ...
|
||||
def __call__(self, value: Any) -> None: ...
|
||||
def compare(self, a: bool, b: bool) -> bool: ...
|
||||
def compare(self, a: Any, b: Any) -> bool: ...
|
||||
def clean(self, x: Any) -> Any: ...
|
||||
|
||||
class MaxValueValidator(BaseValidator):
|
||||
message: Any = ...
|
||||
code: str = ...
|
||||
def compare(self, a: Union[datetime, Decimal, float], b: Union[datetime, Decimal, float]) -> bool: ...
|
||||
|
||||
class MinValueValidator(BaseValidator):
|
||||
message: Any = ...
|
||||
code: str = ...
|
||||
def compare(self, a: Union[datetime, Decimal, float], b: Union[datetime, Decimal, float]) -> bool: ...
|
||||
|
||||
class MinLengthValidator(BaseValidator):
|
||||
message: Any = ...
|
||||
code: str = ...
|
||||
def compare(self, a: int, b: int) -> bool: ...
|
||||
def clean(self, x: str) -> int: ...
|
||||
|
||||
class MaxLengthValidator(BaseValidator):
|
||||
message: Any = ...
|
||||
code: str = ...
|
||||
def compare(self, a: int, b: int) -> bool: ...
|
||||
def clean(self, x: Union[bytes, str]) -> int: ...
|
||||
class MaxValueValidator(BaseValidator): ...
|
||||
class MinValueValidator(BaseValidator): ...
|
||||
class MinLengthValidator(BaseValidator): ...
|
||||
class MaxLengthValidator(BaseValidator): ...
|
||||
|
||||
class DecimalValidator:
|
||||
messages: Any = ...
|
||||
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: ...
|
||||
def __call__(self, value: Decimal) -> None: ...
|
||||
|
||||
class FileExtensionValidator:
|
||||
message: Any = ...
|
||||
message: str = ...
|
||||
code: str = ...
|
||||
allowed_extensions: List[str] = ...
|
||||
def __init__(
|
||||
self,
|
||||
allowed_extensions: Optional[Collection[str]] = ...,
|
||||
message: Optional[str] = ...,
|
||||
message: Optional[_ErrorMessage] = ...,
|
||||
code: Optional[str] = ...,
|
||||
) -> None: ...
|
||||
def __call__(self, value: File) -> None: ...
|
||||
@@ -130,7 +115,7 @@ def get_available_image_extensions() -> List[str]: ...
|
||||
def validate_image_file_extension(value: File) -> None: ...
|
||||
|
||||
class ProhibitNullCharactersValidator:
|
||||
message: Any = ...
|
||||
message: str = ...
|
||||
code: str = ...
|
||||
def __init__(self, message: Optional[str] = ..., code: Optional[str] = ...) -> None: ...
|
||||
def __call__(self, value: Optional[Union[Dict[Any, Any], str, UUID]]) -> None: ...
|
||||
def __init__(self, message: Optional[_ErrorMessage] = ..., code: Optional[str] = ...) -> None: ...
|
||||
def __call__(self, value: Any) -> None: ...
|
||||
|
||||
@@ -10,3 +10,5 @@ class DatabaseWrapper(BaseDatabaseWrapper): ...
|
||||
FORMAT_QMARK_REGEX: Any
|
||||
|
||||
class SQLiteCursorWrapper(Database.Cursor): ...
|
||||
|
||||
def check_sqlite_version() -> None: ...
|
||||
|
||||
@@ -1,21 +1,13 @@
|
||||
from typing import Any, DefaultDict, Dict, Iterator, List, Optional, Sequence, Tuple, Type, Union, Set
|
||||
from typing import Any, Dict, Iterator, List, Optional, Sequence, Tuple, Type, Union, Set
|
||||
|
||||
from django.apps import AppConfig
|
||||
from django.apps.registry import Apps
|
||||
from django.db.models.base import Model
|
||||
from django.db.models.manager import Manager
|
||||
|
||||
from django.db.models.fields import Field
|
||||
|
||||
class AppConfigStub:
|
||||
apps: None
|
||||
label: str
|
||||
models: None
|
||||
models_module: None
|
||||
module: None
|
||||
name: str
|
||||
verbose_name: str
|
||||
def __init__(self, label: str) -> None: ...
|
||||
def import_models(self) -> None: ...
|
||||
class AppConfigStub(AppConfig): ...
|
||||
|
||||
class ModelState:
|
||||
name: str
|
||||
@@ -66,13 +58,7 @@ class ProjectState:
|
||||
def remove_model(self, app_label: str, model_name: str) -> None: ...
|
||||
|
||||
class StateApps(Apps):
|
||||
all_models: DefaultDict
|
||||
apps_ready: bool
|
||||
loading: bool
|
||||
models_ready: bool
|
||||
ready: bool
|
||||
real_models: List[ModelState]
|
||||
stored_app_configs: List[Any]
|
||||
def __init__(
|
||||
self, real_apps: List[str], models: Dict[Tuple[str, str], ModelState], ignore_swappable: bool = ...
|
||||
) -> None: ...
|
||||
|
||||
@@ -2,7 +2,7 @@ from typing import Any, Callable, Dict, List, Optional, Sequence, Set, Tuple, Ty
|
||||
|
||||
from django.core.checks.messages import CheckMessage
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db.models.manager import Manager
|
||||
from django.db.models.manager import BaseManager
|
||||
from django.db.models.options import Options
|
||||
|
||||
_Self = TypeVar("_Self", bound="Model")
|
||||
@@ -13,9 +13,9 @@ class Model(metaclass=ModelBase):
|
||||
class DoesNotExist(Exception): ...
|
||||
class MultipleObjectsReturned(Exception): ...
|
||||
class Meta: ...
|
||||
_default_manager: Manager[Model]
|
||||
_meta: Options[Any]
|
||||
objects: Manager[Any]
|
||||
_default_manager: BaseManager[Model]
|
||||
objects: BaseManager[Any]
|
||||
pk: Any = ...
|
||||
def __init__(self: _Self, *args, **kwargs) -> None: ...
|
||||
def delete(self, using: Any = ..., keep_parents: bool = ...) -> Tuple[int, Dict[str, int]]: ...
|
||||
@@ -41,7 +41,7 @@ class Model(metaclass=ModelBase):
|
||||
using: Optional[str] = ...,
|
||||
update_fields: Optional[Union[Sequence[str], str]] = ...,
|
||||
): ...
|
||||
def refresh_from_db(self: _Self, using: Optional[str] = ..., fields: Optional[List[str]] = ...) -> _Self: ...
|
||||
def refresh_from_db(self: _Self, using: Optional[str] = ..., fields: Optional[List[str]] = ...) -> None: ...
|
||||
def get_deferred_fields(self) -> Set[str]: ...
|
||||
@classmethod
|
||||
def check(cls, **kwargs: Any) -> List[CheckMessage]: ...
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from typing import Any, Callable, Iterable, Optional, Union
|
||||
from typing import Any, Callable, Iterable, Optional, Union, Collection, Type
|
||||
|
||||
from django.db.models.base import Model
|
||||
|
||||
@@ -18,4 +18,11 @@ class ProtectedError(IntegrityError): ...
|
||||
|
||||
class Collector:
|
||||
def __init__(self, using: str) -> None: ...
|
||||
def collect(
|
||||
self,
|
||||
objs: Collection[Optional[Model]],
|
||||
source: Optional[Type[Model]] = ...,
|
||||
source_attr: Optional[str] = ...,
|
||||
**kwargs: Any
|
||||
) -> None: ...
|
||||
def can_fast_delete(self, objs: Union[Model, Iterable[Model]], from_field: Optional[Field] = ...) -> bool: ...
|
||||
|
||||
@@ -27,6 +27,8 @@ from django.forms import Field as FormField, Widget
|
||||
|
||||
class NOT_PROVIDED: ...
|
||||
|
||||
BLANK_CHOICE_DASH: List[Tuple[str, str]] = ...
|
||||
|
||||
_Choice = Tuple[Any, Any]
|
||||
_ChoiceNamedGroup = Tuple[str, Iterable[_Choice]]
|
||||
_FieldChoices = Iterable[Union[_Choice, _ChoiceNamedGroup]]
|
||||
@@ -63,7 +65,7 @@ class Field(RegisterLookupMixin, Generic[_ST, _GT]):
|
||||
null: bool = ...
|
||||
editable: bool = ...
|
||||
empty_strings_allowed: bool = ...
|
||||
choices: Optional[_FieldChoices] = ...
|
||||
choices: _FieldChoices = ...
|
||||
db_column: Optional[str]
|
||||
column: str
|
||||
default: Any
|
||||
@@ -106,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: ...
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from pathlib import Path
|
||||
from typing import Any, Callable, Iterable, List, Optional, Tuple, Type, TypeVar, Union, overload
|
||||
from typing import Any, Callable, Iterable, Optional, Type, TypeVar, Union, overload
|
||||
|
||||
from django.core.files.base import File
|
||||
from django.core.files.images import ImageFile
|
||||
@@ -8,8 +8,6 @@ from django.db.models.base import Model
|
||||
|
||||
from django.db.models.fields import Field, _FieldChoices, _ValidatorCallable, _ErrorMessagesToOverride
|
||||
|
||||
BLANK_CHOICE_DASH: List[Tuple[str, str]] = ...
|
||||
|
||||
class FieldFile(File):
|
||||
instance: Model = ...
|
||||
field: FileField = ...
|
||||
|
||||
@@ -14,9 +14,14 @@ from .text import (
|
||||
Trim as Trim,
|
||||
Ord as Ord,
|
||||
Repeat as Repeat,
|
||||
SHA1 as SHA1,
|
||||
SHA224 as SHA224,
|
||||
SHA256 as SHA256,
|
||||
SHA384 as SHA384,
|
||||
SHA512 as SHA512,
|
||||
StrIndex as StrIndex,
|
||||
Replace as Replace,
|
||||
Substr as Substr,
|
||||
Replace as Replace,
|
||||
Reverse as Reverse,
|
||||
)
|
||||
|
||||
@@ -81,6 +86,7 @@ from .math import (
|
||||
Power as Power,
|
||||
Radians as Radians,
|
||||
Round as Round,
|
||||
Sign as Sign,
|
||||
Sin as Sin,
|
||||
Sqrt as Sqrt,
|
||||
Tan as Tan,
|
||||
|
||||
@@ -23,3 +23,4 @@ class Round(Transform): ...
|
||||
class Sin(NumericOutputFieldMixin, Transform): ...
|
||||
class Sqrt(NumericOutputFieldMixin, Transform): ...
|
||||
class Tan(NumericOutputFieldMixin, Transform): ...
|
||||
class Sign(Transform): ...
|
||||
|
||||
@@ -55,3 +55,11 @@ class Substr(Func):
|
||||
class Trim(Transform): ...
|
||||
class Upper(Transform): ...
|
||||
class Reverse(Transform): ...
|
||||
class MySQLSHA2Mixin: ...
|
||||
class OracleHashMixin: ...
|
||||
class PostgreSQLSHAMixin: ...
|
||||
class SHA1(OracleHashMixin, PostgreSQLSHAMixin, Transform): ...
|
||||
class SHA224(MySQLSHA2Mixin, PostgreSQLSHAMixin, Transform): ...
|
||||
class SHA256(MySQLSHA2Mixin, OracleHashMixin, PostgreSQLSHAMixin, Transform): ...
|
||||
class SHA384(MySQLSHA2Mixin, OracleHashMixin, PostgreSQLSHAMixin, Transform): ...
|
||||
class SHA512(MySQLSHA2Mixin, OracleHashMixin, PostgreSQLSHAMixin, Transform): ...
|
||||
|
||||
@@ -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]: ...
|
||||
|
||||
@@ -8,6 +8,7 @@ from django.contrib.postgres.fields.array import ArrayField
|
||||
from django.contrib.postgres.fields.citext import CIText
|
||||
from django.db.backends.sqlite3.base import DatabaseWrapper
|
||||
from django.db.models.base import Model
|
||||
from django.db.models.constraints import BaseConstraint
|
||||
from django.db.models.fields.mixins import FieldCacheMixin
|
||||
from django.db.models.fields.related import ManyToManyField, OneToOneField
|
||||
from django.db.models.fields.reverse_related import ForeignObjectRel
|
||||
@@ -34,6 +35,7 @@ _M = TypeVar("_M", bound=Model)
|
||||
class Options(Generic[_M]):
|
||||
base_manager: Manager
|
||||
concrete_fields: ImmutableList
|
||||
constraints: List[BaseConstraint]
|
||||
default_manager: Manager
|
||||
fields: ImmutableList
|
||||
local_concrete_fields: ImmutableList
|
||||
|
||||
@@ -16,6 +16,7 @@ from typing import (
|
||||
TypeVar,
|
||||
Union,
|
||||
overload,
|
||||
Reversible,
|
||||
)
|
||||
|
||||
from django.db.models.base import Model
|
||||
@@ -30,6 +31,7 @@ _T = TypeVar("_T", bound=models.Model, covariant=True)
|
||||
_QS = TypeVar("_QS", bound="_BaseQuerySet")
|
||||
|
||||
class _BaseQuerySet(Generic[_T], Sized):
|
||||
model: Type[_T]
|
||||
query: Query
|
||||
def __init__(
|
||||
self,
|
||||
@@ -121,13 +123,14 @@ class _BaseQuerySet(Generic[_T], Sized):
|
||||
def db(self) -> str: ...
|
||||
def resolve_expression(self, *args: Any, **kwargs: Any) -> Any: ...
|
||||
|
||||
class QuerySet(_BaseQuerySet[_T], Collection[_T], Sized):
|
||||
class QuerySet(_BaseQuerySet[_T], Collection[_T], Reversible[_T], Sized):
|
||||
def __iter__(self) -> Iterator[_T]: ...
|
||||
def __contains__(self, x: object) -> bool: ...
|
||||
@overload
|
||||
def __getitem__(self, i: int) -> _T: ...
|
||||
@overload
|
||||
def __getitem__(self: _QS, s: slice) -> _QS: ...
|
||||
def __reversed__(self) -> Iterator[_T]: ...
|
||||
|
||||
_Row = TypeVar("_Row", covariant=True)
|
||||
|
||||
|
||||
@@ -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]: ...
|
||||
|
||||
@@ -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 = ...
|
||||
@@ -205,10 +205,9 @@ class CallableChoiceIterator:
|
||||
def __iter__(self) -> None: ...
|
||||
|
||||
class ChoiceField(Field):
|
||||
choices: Any = ...
|
||||
def __init__(
|
||||
self,
|
||||
choices: Any = ...,
|
||||
choices: Union[_FieldChoices, Callable[[], _FieldChoices]] = ...,
|
||||
required: bool = ...,
|
||||
widget: Optional[Union[Widget, Type[Widget]]] = ...,
|
||||
label: Optional[Any] = ...,
|
||||
|
||||
@@ -1,5 +1,21 @@
|
||||
from datetime import datetime
|
||||
from typing import Any, Callable, Dict, Iterator, List, Mapping, MutableMapping, Optional, Sequence, Tuple, Type, Union
|
||||
from typing import (
|
||||
Any,
|
||||
Callable,
|
||||
Dict,
|
||||
Iterator,
|
||||
List,
|
||||
Mapping,
|
||||
MutableMapping,
|
||||
Optional,
|
||||
Sequence,
|
||||
Tuple,
|
||||
Type,
|
||||
Union,
|
||||
ClassVar,
|
||||
Container,
|
||||
TypeVar,
|
||||
)
|
||||
from unittest.mock import MagicMock
|
||||
from uuid import UUID
|
||||
|
||||
@@ -24,6 +40,11 @@ _Fields = Union[List[Union[Callable, str]], Sequence[str], Literal["__all__"]]
|
||||
_Labels = Dict[str, str]
|
||||
_ErrorMessages = Dict[str, Dict[str, str]]
|
||||
|
||||
_M = TypeVar("_M", bound=Model)
|
||||
|
||||
def construct_instance(
|
||||
form: BaseForm, instance: _M, fields: Optional[Container[str]] = ..., exclude: Optional[Container[str]] = ...
|
||||
) -> _M: ...
|
||||
def model_to_dict(
|
||||
instance: Model, fields: Optional[_Fields] = ..., exclude: Optional[_Fields] = ...
|
||||
) -> Dict[str, Any]: ...
|
||||
@@ -76,7 +97,8 @@ class BaseModelForm(BaseForm):
|
||||
save_m2m: Any = ...
|
||||
def save(self, commit: bool = ...) -> Any: ...
|
||||
|
||||
class ModelForm(BaseModelForm): ...
|
||||
class ModelForm(BaseModelForm, metaclass=ModelFormMetaclass):
|
||||
base_fields: ClassVar[Dict[str, Field]] = ...
|
||||
|
||||
def modelform_factory(
|
||||
model: Type[Model],
|
||||
|
||||
@@ -18,6 +18,7 @@ from typing import (
|
||||
|
||||
from django.contrib.auth.base_user import AbstractBaseUser
|
||||
from django.contrib.sessions.backends.base import SessionBase
|
||||
from django.contrib.sites.models import Site
|
||||
from django.utils.datastructures import CaseInsensitiveMapping, ImmutableList, MultiValueDict
|
||||
|
||||
from django.core.files import uploadedfile, uploadhandler
|
||||
@@ -51,6 +52,7 @@ class HttpRequest(BytesIO):
|
||||
content_type: Optional[str] = ...
|
||||
content_params: Optional[Dict[str, str]] = ...
|
||||
user: AbstractBaseUser
|
||||
site: Site
|
||||
session: SessionBase
|
||||
encoding: Optional[str] = ...
|
||||
upload_handlers: UploadHandlerList = ...
|
||||
|
||||
@@ -62,28 +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 content(self) -> Any: ...
|
||||
@content.setter
|
||||
def content(self, value: Any) -> None: ...
|
||||
@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
|
||||
@@ -107,7 +106,7 @@ class FileResponse(StreamingHttpResponse):
|
||||
def json(self) -> Dict[str, Any]: ...
|
||||
|
||||
class HttpResponseRedirectBase(HttpResponse):
|
||||
allowed_schemes = ... # type: List[str]
|
||||
allowed_schemes: List[str] = ...
|
||||
def __init__(self, redirect_to: str, *args: Any, **kwargs: Any) -> None: ...
|
||||
|
||||
class HttpResponseRedirect(HttpResponseRedirectBase): ...
|
||||
|
||||
@@ -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: ...
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import threading
|
||||
import unittest
|
||||
from datetime import date
|
||||
from typing import Any, Callable, Dict, Iterator, List, Optional, Set, Tuple, Type, Union, ClassVar
|
||||
from typing import Any, Callable, Dict, Iterator, List, Optional, Set, Tuple, Type, Union, ClassVar, overload
|
||||
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.core.handlers.wsgi import WSGIHandler
|
||||
@@ -146,9 +146,14 @@ class TransactionTestCase(SimpleTestCase):
|
||||
ordered: bool = ...,
|
||||
msg: Optional[str] = ...,
|
||||
) -> None: ...
|
||||
@overload
|
||||
def assertNumQueries(
|
||||
self, num: int, func: Optional[Union[Callable, Type[list]]] = ..., *args: Any, using: Any = ..., **kwargs: Any
|
||||
) -> Optional[_AssertNumQueriesContext]: ...
|
||||
self, num: int, func: Callable[..., Any], *args: Any, using: str = ..., **kwargs: Any
|
||||
) -> None: ...
|
||||
@overload
|
||||
def assertNumQueries(
|
||||
self, num: int, func: None = ..., *args: Any, using: str = ..., **kwargs: Any
|
||||
) -> _AssertNumQueriesContext: ...
|
||||
|
||||
class TestCase(TransactionTestCase):
|
||||
@classmethod
|
||||
|
||||
@@ -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] = ...
|
||||
|
||||
@@ -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: ...
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -1,25 +1,59 @@
|
||||
from typing import Any, Optional
|
||||
import datetime
|
||||
from decimal import Decimal
|
||||
from typing import Any, TypeVar, overload, Union
|
||||
|
||||
from django.utils.functional import Promise
|
||||
from typing_extensions import Literal
|
||||
|
||||
class DjangoUnicodeDecodeError(UnicodeDecodeError):
|
||||
obj: bytes = ...
|
||||
def __init__(self, obj: bytes, *args: Any) -> None: ...
|
||||
|
||||
python_2_unicode_compatible: Any
|
||||
|
||||
_P = TypeVar("_P", bound=Promise)
|
||||
_S = TypeVar("_S", bound=str)
|
||||
_PT = TypeVar("_PT", None, int, float, Decimal, datetime.datetime, datetime.date, datetime.time)
|
||||
@overload
|
||||
def smart_text(s: _P, encoding: str = ..., strings_only: bool = ..., errors: str = ...) -> _P: ...
|
||||
@overload
|
||||
def smart_text(s: _PT, encoding: str = ..., strings_only: Literal[True] = ..., errors: str = ...) -> _PT: ...
|
||||
@overload
|
||||
def smart_text(s: _S, encoding: str = ..., strings_only: bool = ..., errors: str = ...) -> _S: ...
|
||||
@overload
|
||||
def smart_text(s: Any, encoding: str = ..., strings_only: bool = ..., errors: str = ...) -> str: ...
|
||||
def is_protected_type(obj: Any) -> bool: ...
|
||||
def force_text(s: Any, encoding: str = ..., strings_only: bool = ..., errors: str = ...) -> Optional[str]: ...
|
||||
@overload
|
||||
def force_text(s: _PT, encoding: str = ..., strings_only: Literal[True] = ..., errors: str = ...) -> _PT: ...
|
||||
@overload
|
||||
def force_text(s: _S, encoding: str = ..., strings_only: bool = ..., errors: str = ...) -> _S: ...
|
||||
@overload
|
||||
def force_text(s: Any, encoding: str = ..., strings_only: bool = ..., errors: str = ...) -> str: ...
|
||||
@overload
|
||||
def smart_bytes(s: _P, encoding: str = ..., strings_only: bool = ..., errors: str = ...) -> _P: ...
|
||||
@overload
|
||||
def smart_bytes(s: _PT, encoding: str = ..., strings_only: Literal[True] = ..., errors: str = ...) -> _PT: ...
|
||||
@overload
|
||||
def smart_bytes(s: Any, encoding: str = ..., strings_only: bool = ..., errors: str = ...) -> bytes: ...
|
||||
@overload
|
||||
def force_bytes(s: _PT, encoding: str = ..., strings_only: Literal[True] = ..., errors: str = ...) -> _PT: ...
|
||||
@overload
|
||||
def force_bytes(s: Any, encoding: str = ..., strings_only: bool = ..., errors: str = ...) -> bytes: ...
|
||||
|
||||
smart_str = smart_text
|
||||
force_str = force_text
|
||||
|
||||
def iri_to_uri(iri: Optional[str]) -> Optional[str]: ...
|
||||
def uri_to_iri(uri: Optional[str]) -> Optional[str]: ...
|
||||
@overload
|
||||
def iri_to_uri(iri: None) -> None: ...
|
||||
@overload
|
||||
def iri_to_uri(iri: Union[str, Promise]) -> str: ...
|
||||
@overload
|
||||
def uri_to_iri(iri: None) -> None: ...
|
||||
@overload
|
||||
def uri_to_iri(iri: str) -> str: ...
|
||||
def escape_uri_path(path: str) -> str: ...
|
||||
def repercent_broken_unicode(path: bytes) -> bytes: ...
|
||||
def filepath_to_uri(path: Optional[str]) -> Optional[str]: ...
|
||||
@overload
|
||||
def filepath_to_uri(path: None) -> None: ...
|
||||
@overload
|
||||
def filepath_to_uri(path: str) -> str: ...
|
||||
def get_system_encoding() -> str: ...
|
||||
|
||||
DEFAULT_LOCALE_ENCODING: Any
|
||||
|
||||
@@ -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: ...
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ class Node:
|
||||
connector: str = ...
|
||||
negated: bool = ...
|
||||
def __init__(
|
||||
self, children: Optional[_NodeChildren] = ..., connector: Optional[str] = ..., negated: bool = ...,
|
||||
self, children: Optional[_NodeChildren] = ..., connector: Optional[str] = ..., negated: bool = ...
|
||||
) -> None: ...
|
||||
def __deepcopy__(self, memodict: Dict[Any, Any]) -> Node: ...
|
||||
def __len__(self) -> int: ...
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
from typing import Any, Callable, Dict, Optional, Sequence, Type, Union
|
||||
|
||||
from django.forms.forms import BaseForm
|
||||
from django.http import HttpRequest, HttpResponse
|
||||
from django.views.generic.base import ContextMixin, TemplateResponseMixin, View
|
||||
from django.views.generic.detail import BaseDetailView, SingleObjectMixin, SingleObjectTemplateResponseMixin
|
||||
from typing_extensions import Literal
|
||||
|
||||
from django.http import HttpRequest, HttpResponse
|
||||
|
||||
class FormMixin(ContextMixin):
|
||||
initial: Dict[str, Any] = ...
|
||||
form_class: Optional[Type[BaseForm]] = ...
|
||||
|
||||
@@ -124,7 +124,7 @@ class DjangoContext:
|
||||
if isinstance(field, ForeignObjectRel):
|
||||
yield field
|
||||
|
||||
def get_field_lookup_exact_type(self, api: TypeChecker, field: Field) -> MypyType:
|
||||
def get_field_lookup_exact_type(self, api: TypeChecker, field: Union[Field, ForeignObjectRel]) -> MypyType:
|
||||
if isinstance(field, (RelatedField, ForeignObjectRel)):
|
||||
related_model_cls = field.related_model
|
||||
primary_key_field = self.get_primary_key_field(related_model_cls)
|
||||
@@ -134,10 +134,8 @@ class DjangoContext:
|
||||
if rel_model_info is None:
|
||||
return AnyType(TypeOfAny.explicit)
|
||||
|
||||
model_and_primary_key_type = UnionType.make_union([Instance(rel_model_info, []),
|
||||
primary_key_type])
|
||||
model_and_primary_key_type = UnionType.make_union([Instance(rel_model_info, []), primary_key_type])
|
||||
return helpers.make_optional(model_and_primary_key_type)
|
||||
# return helpers.make_optional(Instance(rel_model_info, []))
|
||||
|
||||
field_info = helpers.lookup_class_typeinfo(api, field.__class__)
|
||||
if field_info is None:
|
||||
@@ -228,21 +226,22 @@ class DjangoContext:
|
||||
attname = field.attname
|
||||
return attname
|
||||
|
||||
def get_field_nullability(self, field: Field, method: Optional[str]) -> bool:
|
||||
def get_field_nullability(self, field: Union[Field, ForeignObjectRel], method: Optional[str]) -> bool:
|
||||
nullable = field.null
|
||||
if not nullable and isinstance(field, CharField) and field.blank:
|
||||
return True
|
||||
if method == '__init__':
|
||||
if field.primary_key or isinstance(field, ForeignKey):
|
||||
if ((isinstance(field, Field) and field.primary_key)
|
||||
or isinstance(field, ForeignKey)):
|
||||
return True
|
||||
if method == 'create':
|
||||
if isinstance(field, AutoField):
|
||||
return True
|
||||
if field.has_default():
|
||||
if isinstance(field, Field) and field.has_default():
|
||||
return True
|
||||
return nullable
|
||||
|
||||
def get_field_set_type(self, api: TypeChecker, field: Field, *, method: str) -> MypyType:
|
||||
def get_field_set_type(self, api: TypeChecker, field: Union[Field, ForeignObjectRel], *, method: str) -> MypyType:
|
||||
""" Get a type of __set__ for this specific Django field. """
|
||||
target_field = field
|
||||
if isinstance(field, ForeignKey):
|
||||
@@ -259,7 +258,7 @@ class DjangoContext:
|
||||
field_set_type = helpers.convert_any_to_type(field_set_type, argument_field_type)
|
||||
return field_set_type
|
||||
|
||||
def get_field_get_type(self, api: TypeChecker, field: Field, *, method: str) -> MypyType:
|
||||
def get_field_get_type(self, api: TypeChecker, field: Union[Field, ForeignObjectRel], *, method: str) -> MypyType:
|
||||
""" Get a type of __get__ for this specific Django field. """
|
||||
field_info = helpers.lookup_class_typeinfo(api, field.__class__)
|
||||
if field_info is None:
|
||||
@@ -303,7 +302,10 @@ class DjangoContext:
|
||||
|
||||
return related_model_cls
|
||||
|
||||
def _resolve_field_from_parts(self, field_parts: Iterable[str], model_cls: Type[Model]) -> Field:
|
||||
def _resolve_field_from_parts(self,
|
||||
field_parts: Iterable[str],
|
||||
model_cls: Type[Model]
|
||||
) -> Union[Field, ForeignObjectRel]:
|
||||
currently_observed_model = model_cls
|
||||
field = None
|
||||
for field_part in field_parts:
|
||||
@@ -325,7 +327,7 @@ class DjangoContext:
|
||||
assert field is not None
|
||||
return field
|
||||
|
||||
def resolve_lookup_into_field(self, model_cls: Type[Model], lookup: str) -> Field:
|
||||
def resolve_lookup_into_field(self, model_cls: Type[Model], lookup: str) -> Union[Field, ForeignObjectRel]:
|
||||
query = Query(model_cls)
|
||||
lookup_parts, field_parts, is_expression = query.solve_lookup_type(lookup)
|
||||
if lookup_parts:
|
||||
|
||||
@@ -13,7 +13,7 @@ DUMMY_SETTINGS_BASE_CLASS = 'django.conf._DjangoConfLazyObject'
|
||||
QUERYSET_CLASS_FULLNAME = 'django.db.models.query.QuerySet'
|
||||
BASE_MANAGER_CLASS_FULLNAME = 'django.db.models.manager.BaseManager'
|
||||
MANAGER_CLASS_FULLNAME = 'django.db.models.manager.Manager'
|
||||
RELATED_MANAGER_CLASS_FULLNAME = 'django.db.models.manager.RelatedManager'
|
||||
RELATED_MANAGER_CLASS = 'django.db.models.manager.RelatedManager'
|
||||
|
||||
BASEFORM_CLASS_FULLNAME = 'django.forms.forms.BaseForm'
|
||||
FORM_CLASS_FULLNAME = 'django.forms.forms.Form'
|
||||
@@ -23,9 +23,7 @@ FORM_MIXIN_CLASS_FULLNAME = 'django.views.generic.edit.FormMixin'
|
||||
|
||||
MANAGER_CLASSES = {
|
||||
MANAGER_CLASS_FULLNAME,
|
||||
RELATED_MANAGER_CLASS_FULLNAME,
|
||||
BASE_MANAGER_CLASS_FULLNAME,
|
||||
# QUERYSET_CLASS_FULLNAME
|
||||
}
|
||||
|
||||
RELATED_FIELDS_CLASSES = {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from collections import OrderedDict
|
||||
from typing import (
|
||||
TYPE_CHECKING, Any, Dict, Iterable, Iterator, List, Optional, Set, Union, cast,
|
||||
TYPE_CHECKING, Any, Dict, Iterable, Iterator, List, Optional, Set, Tuple, Union,
|
||||
)
|
||||
|
||||
from django.db.models.fields import Field
|
||||
@@ -10,13 +10,15 @@ from mypy import checker
|
||||
from mypy.checker import TypeChecker
|
||||
from mypy.mro import calculate_mro
|
||||
from mypy.nodes import (
|
||||
GDEF, MDEF, Block, ClassDef, Expression, MemberExpr, MypyFile, NameExpr, StrExpr, SymbolNode, SymbolTable,
|
||||
SymbolTableNode, TypeInfo, Var,
|
||||
GDEF, MDEF, Argument, Block, ClassDef, Expression, FuncDef, MemberExpr, MypyFile, NameExpr, PlaceholderNode,
|
||||
StrExpr, SymbolNode, SymbolTable, SymbolTableNode, TypeInfo, Var,
|
||||
)
|
||||
from mypy.plugin import (
|
||||
AttributeContext, CheckerPluginInterface, FunctionContext, MethodContext,
|
||||
AttributeContext, CheckerPluginInterface, ClassDefContext, DynamicClassDefContext, FunctionContext, MethodContext,
|
||||
)
|
||||
from mypy.types import AnyType, Instance, NoneTyp, TupleType
|
||||
from mypy.plugins.common import add_method
|
||||
from mypy.semanal import SemanticAnalyzer
|
||||
from mypy.types import AnyType, CallableType, Instance, NoneTyp, TupleType
|
||||
from mypy.types import Type as MypyType
|
||||
from mypy.types import TypedDictType, TypeOfAny, UnionType
|
||||
|
||||
@@ -55,7 +57,7 @@ def lookup_fully_qualified_generic(name: str, all_modules: Dict[str, MypyFile])
|
||||
return sym.node
|
||||
|
||||
|
||||
def lookup_fully_qualified_typeinfo(api: TypeChecker, fullname: str) -> Optional[TypeInfo]:
|
||||
def lookup_fully_qualified_typeinfo(api: Union[TypeChecker, SemanticAnalyzer], fullname: str) -> Optional[TypeInfo]:
|
||||
node = lookup_fully_qualified_generic(fullname, api.modules)
|
||||
if not isinstance(node, TypeInfo):
|
||||
return None
|
||||
@@ -173,8 +175,11 @@ def get_nested_meta_node_for_current_class(info: TypeInfo) -> Optional[TypeInfo]
|
||||
return None
|
||||
|
||||
|
||||
def add_new_class_for_module(module: MypyFile, name: str, bases: List[Instance],
|
||||
fields: 'OrderedDict[str, MypyType]') -> TypeInfo:
|
||||
def add_new_class_for_module(module: MypyFile,
|
||||
name: str,
|
||||
bases: List[Instance],
|
||||
fields: Optional[Dict[str, MypyType]] = None
|
||||
) -> TypeInfo:
|
||||
new_class_unique_name = checker.gen_unique_name(name, module.names)
|
||||
|
||||
# make new class expression
|
||||
@@ -188,11 +193,12 @@ def add_new_class_for_module(module: MypyFile, name: str, bases: List[Instance],
|
||||
new_typeinfo.calculate_metaclass_type()
|
||||
|
||||
# add fields
|
||||
for field_name, field_type in fields.items():
|
||||
var = Var(field_name, type=field_type)
|
||||
var.info = new_typeinfo
|
||||
var._fullname = new_typeinfo.fullname + '.' + field_name
|
||||
new_typeinfo.names[field_name] = SymbolTableNode(MDEF, var, plugin_generated=True)
|
||||
if fields:
|
||||
for field_name, field_type in fields.items():
|
||||
var = Var(field_name, type=field_type)
|
||||
var.info = new_typeinfo
|
||||
var._fullname = new_typeinfo.fullname + '.' + field_name
|
||||
new_typeinfo.names[field_name] = SymbolTableNode(MDEF, var, plugin_generated=True)
|
||||
|
||||
classdef.info = new_typeinfo
|
||||
module.names[new_class_unique_name] = SymbolTableNode(GDEF, new_typeinfo, plugin_generated=True)
|
||||
@@ -253,8 +259,7 @@ def make_typeddict(api: CheckerPluginInterface, fields: 'OrderedDict[str, MypyTy
|
||||
return typed_dict_type
|
||||
|
||||
|
||||
def resolve_string_attribute_value(attr_expr: Expression, ctx: Union[FunctionContext, MethodContext],
|
||||
django_context: 'DjangoContext') -> Optional[str]:
|
||||
def resolve_string_attribute_value(attr_expr: Expression, django_context: 'DjangoContext') -> Optional[str]:
|
||||
if isinstance(attr_expr, StrExpr):
|
||||
return attr_expr.value
|
||||
|
||||
@@ -264,15 +269,19 @@ def resolve_string_attribute_value(attr_expr: Expression, ctx: Union[FunctionCon
|
||||
if isinstance(attr_expr.expr, NameExpr) and attr_expr.expr.fullname == 'django.conf.settings':
|
||||
if hasattr(django_context.settings, member_name):
|
||||
return getattr(django_context.settings, member_name)
|
||||
|
||||
ctx.api.fail(f'Expression of type {type(attr_expr).__name__!r} is not supported', ctx.context)
|
||||
return None
|
||||
|
||||
|
||||
def get_semanal_api(ctx: Union[ClassDefContext, DynamicClassDefContext]) -> SemanticAnalyzer:
|
||||
if not isinstance(ctx.api, SemanticAnalyzer):
|
||||
raise ValueError('Not a SemanticAnalyzer')
|
||||
return ctx.api
|
||||
|
||||
|
||||
def get_typechecker_api(ctx: Union[AttributeContext, MethodContext, FunctionContext]) -> TypeChecker:
|
||||
if not isinstance(ctx.api, TypeChecker):
|
||||
raise ValueError('Not a TypeChecker')
|
||||
return cast(TypeChecker, ctx.api)
|
||||
return ctx.api
|
||||
|
||||
|
||||
def is_model_subclass_info(info: TypeInfo, django_context: 'DjangoContext') -> bool:
|
||||
@@ -298,3 +307,69 @@ def add_new_sym_for_info(info: TypeInfo, *, name: str, sym_type: MypyType) -> No
|
||||
var.is_inferred = True
|
||||
info.names[name] = SymbolTableNode(MDEF, var,
|
||||
plugin_generated=True)
|
||||
|
||||
|
||||
def build_unannotated_method_args(method_node: FuncDef) -> Tuple[List[Argument], MypyType]:
|
||||
prepared_arguments = []
|
||||
for argument in method_node.arguments[1:]:
|
||||
argument.type_annotation = AnyType(TypeOfAny.unannotated)
|
||||
prepared_arguments.append(argument)
|
||||
return_type = AnyType(TypeOfAny.unannotated)
|
||||
return prepared_arguments, return_type
|
||||
|
||||
|
||||
def copy_method_to_another_class(ctx: ClassDefContext, self_type: Instance,
|
||||
new_method_name: str, method_node: FuncDef) -> None:
|
||||
semanal_api = get_semanal_api(ctx)
|
||||
if method_node.type is None:
|
||||
if not semanal_api.final_iteration:
|
||||
semanal_api.defer()
|
||||
return
|
||||
|
||||
arguments, return_type = build_unannotated_method_args(method_node)
|
||||
add_method(ctx,
|
||||
new_method_name,
|
||||
args=arguments,
|
||||
return_type=return_type,
|
||||
self_type=self_type)
|
||||
return
|
||||
|
||||
method_type = method_node.type
|
||||
if not isinstance(method_type, CallableType):
|
||||
if not semanal_api.final_iteration:
|
||||
semanal_api.defer()
|
||||
return
|
||||
|
||||
arguments = []
|
||||
bound_return_type = semanal_api.anal_type(method_type.ret_type,
|
||||
allow_placeholder=True)
|
||||
assert bound_return_type is not None
|
||||
|
||||
if isinstance(bound_return_type, PlaceholderNode):
|
||||
return
|
||||
|
||||
for arg_name, arg_type, original_argument in zip(method_type.arg_names[1:],
|
||||
method_type.arg_types[1:],
|
||||
method_node.arguments[1:]):
|
||||
bound_arg_type = semanal_api.anal_type(arg_type, allow_placeholder=True)
|
||||
assert bound_arg_type is not None
|
||||
|
||||
if isinstance(bound_arg_type, PlaceholderNode):
|
||||
return
|
||||
|
||||
var = Var(name=original_argument.variable.name,
|
||||
type=arg_type)
|
||||
var.line = original_argument.variable.line
|
||||
var.column = original_argument.variable.column
|
||||
argument = Argument(variable=var,
|
||||
type_annotation=bound_arg_type,
|
||||
initializer=original_argument.initializer,
|
||||
kind=original_argument.kind)
|
||||
argument.set_line(original_argument)
|
||||
arguments.append(argument)
|
||||
|
||||
add_method(ctx,
|
||||
new_method_name,
|
||||
args=arguments,
|
||||
return_type=bound_return_type,
|
||||
self_type=self_type)
|
||||
|
||||
@@ -7,7 +7,7 @@ from mypy.errors import Errors
|
||||
from mypy.nodes import MypyFile, TypeInfo
|
||||
from mypy.options import Options
|
||||
from mypy.plugin import (
|
||||
AttributeContext, ClassDefContext, FunctionContext, MethodContext, Plugin,
|
||||
AttributeContext, ClassDefContext, DynamicClassDefContext, FunctionContext, MethodContext, Plugin,
|
||||
)
|
||||
from mypy.types import Type as MypyType
|
||||
|
||||
@@ -17,6 +17,9 @@ from mypy_django_plugin.lib import fullnames, helpers
|
||||
from mypy_django_plugin.transformers import (
|
||||
fields, forms, init_create, meta, querysets, request, settings,
|
||||
)
|
||||
from mypy_django_plugin.transformers.managers import (
|
||||
create_new_manager_class_from_from_queryset_method,
|
||||
)
|
||||
from mypy_django_plugin.transformers.models import process_model_class
|
||||
|
||||
|
||||
@@ -242,6 +245,15 @@ class NewSemanalDjangoPlugin(Plugin):
|
||||
return partial(request.set_auth_user_model_as_type_for_request_user, django_context=self.django_context)
|
||||
return None
|
||||
|
||||
def get_dynamic_class_hook(self, fullname: str
|
||||
) -> Optional[Callable[[DynamicClassDefContext], None]]:
|
||||
if fullname.endswith('from_queryset'):
|
||||
class_name, _, _ = fullname.rpartition('.')
|
||||
info = self._get_typeinfo_or_none(class_name)
|
||||
if info and info.has_base(fullnames.BASE_MANAGER_CLASS_FULLNAME):
|
||||
return create_new_manager_class_from_from_queryset_method
|
||||
return None
|
||||
|
||||
|
||||
def plugin(version):
|
||||
return NewSemanalDjangoPlugin
|
||||
|
||||
77
mypy_django_plugin/transformers/managers.py
Normal file
77
mypy_django_plugin/transformers/managers.py
Normal file
@@ -0,0 +1,77 @@
|
||||
from mypy.nodes import (
|
||||
GDEF, FuncDef, MemberExpr, NameExpr, RefExpr, StrExpr, SymbolTableNode, TypeInfo,
|
||||
)
|
||||
from mypy.plugin import ClassDefContext, DynamicClassDefContext
|
||||
from mypy.types import AnyType, Instance, TypeOfAny
|
||||
|
||||
from mypy_django_plugin.lib import fullnames, helpers
|
||||
|
||||
|
||||
def create_new_manager_class_from_from_queryset_method(ctx: DynamicClassDefContext) -> None:
|
||||
semanal_api = helpers.get_semanal_api(ctx)
|
||||
|
||||
callee = ctx.call.callee
|
||||
assert isinstance(callee, MemberExpr)
|
||||
assert isinstance(callee.expr, RefExpr)
|
||||
|
||||
base_manager_info = callee.expr.node
|
||||
if base_manager_info is None:
|
||||
if not semanal_api.final_iteration:
|
||||
semanal_api.defer()
|
||||
return
|
||||
|
||||
assert isinstance(base_manager_info, TypeInfo)
|
||||
new_manager_info = semanal_api.basic_new_typeinfo(ctx.name,
|
||||
basetype_or_fallback=Instance(base_manager_info,
|
||||
[AnyType(TypeOfAny.unannotated)]))
|
||||
new_manager_info.line = ctx.call.line
|
||||
new_manager_info.defn.line = ctx.call.line
|
||||
new_manager_info.metaclass_type = new_manager_info.calculate_metaclass_type()
|
||||
|
||||
current_module = semanal_api.cur_mod_node
|
||||
current_module.names[ctx.name] = SymbolTableNode(GDEF, new_manager_info,
|
||||
plugin_generated=True)
|
||||
passed_queryset = ctx.call.args[0]
|
||||
assert isinstance(passed_queryset, NameExpr)
|
||||
|
||||
derived_queryset_fullname = passed_queryset.fullname
|
||||
assert derived_queryset_fullname is not None
|
||||
|
||||
sym = semanal_api.lookup_fully_qualified_or_none(derived_queryset_fullname)
|
||||
assert sym is not None
|
||||
if sym.node is None:
|
||||
if not semanal_api.final_iteration:
|
||||
semanal_api.defer()
|
||||
else:
|
||||
# inherit from Any to prevent false-positives, if queryset class cannot be resolved
|
||||
new_manager_info.fallback_to_any = True
|
||||
return
|
||||
|
||||
derived_queryset_info = sym.node
|
||||
assert isinstance(derived_queryset_info, TypeInfo)
|
||||
|
||||
if len(ctx.call.args) > 1:
|
||||
expr = ctx.call.args[1]
|
||||
assert isinstance(expr, StrExpr)
|
||||
custom_manager_generated_name = expr.value
|
||||
else:
|
||||
custom_manager_generated_name = base_manager_info.name + 'From' + derived_queryset_info.name
|
||||
|
||||
custom_manager_generated_fullname = '.'.join(['django.db.models.manager', custom_manager_generated_name])
|
||||
if 'from_queryset_managers' not in base_manager_info.metadata:
|
||||
base_manager_info.metadata['from_queryset_managers'] = {}
|
||||
base_manager_info.metadata['from_queryset_managers'][custom_manager_generated_fullname] = new_manager_info.fullname
|
||||
|
||||
class_def_context = ClassDefContext(cls=new_manager_info.defn,
|
||||
reason=ctx.call, api=semanal_api)
|
||||
self_type = Instance(new_manager_info, [])
|
||||
# we need to copy all methods in MRO before django.db.models.query.QuerySet
|
||||
for class_mro_info in derived_queryset_info.mro:
|
||||
if class_mro_info.fullname == fullnames.QUERYSET_CLASS_FULLNAME:
|
||||
break
|
||||
for name, sym in class_mro_info.names.items():
|
||||
if isinstance(sym.node, FuncDef):
|
||||
helpers.copy_method_to_another_class(class_def_context,
|
||||
self_type,
|
||||
new_method_name=name,
|
||||
method_node=sym.node)
|
||||
@@ -36,7 +36,7 @@ def return_proper_field_type_from_get_field(ctx: MethodContext, django_context:
|
||||
if field_name_expr is None:
|
||||
return ctx.default_return_type
|
||||
|
||||
field_name = helpers.resolve_string_attribute_value(field_name_expr, ctx, django_context)
|
||||
field_name = helpers.resolve_string_attribute_value(field_name_expr, django_context)
|
||||
if field_name is None:
|
||||
return ctx.default_return_type
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
from collections import OrderedDict
|
||||
from typing import List, Tuple, Type
|
||||
from typing import Dict, List, Optional, Type, cast
|
||||
|
||||
from django.db.models.base import Model
|
||||
from django.db.models.fields import DateField, DateTimeField
|
||||
@@ -10,8 +9,8 @@ from django.db.models.fields.reverse_related import (
|
||||
from mypy.nodes import ARG_STAR2, Argument, Context, FuncDef, TypeInfo, Var
|
||||
from mypy.plugin import ClassDefContext
|
||||
from mypy.plugins import common
|
||||
from mypy.plugins.common import add_method
|
||||
from mypy.types import AnyType, CallableType, Instance
|
||||
from mypy.semanal import SemanticAnalyzer
|
||||
from mypy.types import AnyType, Instance
|
||||
from mypy.types import Type as MypyType
|
||||
from mypy.types import TypeOfAny
|
||||
|
||||
@@ -22,17 +21,22 @@ from mypy_django_plugin.transformers.fields import get_field_descriptor_types
|
||||
|
||||
|
||||
class ModelClassInitializer:
|
||||
api: SemanticAnalyzer
|
||||
|
||||
def __init__(self, ctx: ClassDefContext, django_context: DjangoContext):
|
||||
self.api = ctx.api
|
||||
self.api = cast(SemanticAnalyzer, ctx.api)
|
||||
self.model_classdef = ctx.cls
|
||||
self.django_context = django_context
|
||||
self.ctx = ctx
|
||||
|
||||
def lookup_typeinfo(self, fullname: str) -> Optional[TypeInfo]:
|
||||
return helpers.lookup_fully_qualified_typeinfo(self.api, fullname)
|
||||
|
||||
def lookup_typeinfo_or_incomplete_defn_error(self, fullname: str) -> TypeInfo:
|
||||
sym = self.api.lookup_fully_qualified_or_none(fullname)
|
||||
if sym is None or not isinstance(sym.node, TypeInfo):
|
||||
info = self.lookup_typeinfo(fullname)
|
||||
if info is None:
|
||||
raise helpers.IncompleteDefnException(f'No {fullname!r} found')
|
||||
return sym.node
|
||||
return info
|
||||
|
||||
def lookup_class_typeinfo_or_incomplete_defn_error(self, klass: type) -> TypeInfo:
|
||||
fullname = helpers.get_class_fullname(klass)
|
||||
@@ -54,6 +58,12 @@ class ModelClassInitializer:
|
||||
name=name,
|
||||
sym_type=typ)
|
||||
|
||||
def add_new_class_for_current_module(self, name: str, bases: List[Instance]) -> TypeInfo:
|
||||
current_module = self.api.modules[self.model_classdef.info.module_name]
|
||||
new_class_info = helpers.add_new_class_for_module(current_module,
|
||||
name=name, bases=bases)
|
||||
return new_class_info
|
||||
|
||||
def run(self) -> None:
|
||||
model_cls = self.django_context.get_model_class_by_fullname(self.model_classdef.fullname)
|
||||
if model_cls is None:
|
||||
@@ -118,7 +128,14 @@ class AddRelatedModelsId(ModelClassInitializer):
|
||||
continue
|
||||
|
||||
rel_primary_key_field = self.django_context.get_primary_key_field(related_model_cls)
|
||||
field_info = self.lookup_class_typeinfo_or_incomplete_defn_error(rel_primary_key_field.__class__)
|
||||
try:
|
||||
field_info = self.lookup_class_typeinfo_or_incomplete_defn_error(rel_primary_key_field.__class__)
|
||||
except helpers.IncompleteDefnException as exc:
|
||||
if not self.api.final_iteration:
|
||||
raise exc
|
||||
else:
|
||||
continue
|
||||
|
||||
is_nullable = self.django_context.get_field_nullability(field, None)
|
||||
set_type, get_type = get_field_descriptor_types(field_info, is_nullable)
|
||||
self.add_new_node_to_model_class(field.attname,
|
||||
@@ -126,78 +143,96 @@ class AddRelatedModelsId(ModelClassInitializer):
|
||||
|
||||
|
||||
class AddManagers(ModelClassInitializer):
|
||||
def _is_manager_any(self, typ: Instance) -> bool:
|
||||
return typ.type.fullname == fullnames.MANAGER_CLASS_FULLNAME and type(typ.args[0]) == AnyType
|
||||
def has_any_parametrized_manager_as_base(self, info: TypeInfo) -> bool:
|
||||
for base in helpers.iter_bases(info):
|
||||
if self.is_any_parametrized_manager(base):
|
||||
return True
|
||||
return False
|
||||
|
||||
def is_any_parametrized_manager(self, typ: Instance) -> bool:
|
||||
return typ.type.fullname in fullnames.MANAGER_CLASSES and isinstance(typ.args[0], AnyType)
|
||||
|
||||
def get_generated_manager_mappings(self, base_manager_fullname: str) -> Dict[str, str]:
|
||||
base_manager_info = self.lookup_typeinfo(base_manager_fullname)
|
||||
if (base_manager_info is None
|
||||
or 'from_queryset_managers' not in base_manager_info.metadata):
|
||||
return {}
|
||||
return base_manager_info.metadata['from_queryset_managers']
|
||||
|
||||
def create_new_model_parametrized_manager(self, name: str, base_manager_info: TypeInfo) -> Instance:
|
||||
bases = []
|
||||
for original_base in base_manager_info.bases:
|
||||
if self.is_any_parametrized_manager(original_base):
|
||||
if original_base.type is None:
|
||||
raise helpers.IncompleteDefnException()
|
||||
|
||||
original_base = helpers.reparametrize_instance(original_base,
|
||||
[Instance(self.model_classdef.info, [])])
|
||||
bases.append(original_base)
|
||||
|
||||
new_manager_info = self.add_new_class_for_current_module(name, bases)
|
||||
# copy fields to a new manager
|
||||
new_cls_def_context = ClassDefContext(cls=new_manager_info.defn,
|
||||
reason=self.ctx.reason,
|
||||
api=self.api)
|
||||
custom_manager_type = Instance(new_manager_info, [Instance(self.model_classdef.info, [])])
|
||||
|
||||
for name, sym in base_manager_info.names.items():
|
||||
# replace self type with new class, if copying method
|
||||
if isinstance(sym.node, FuncDef):
|
||||
helpers.copy_method_to_another_class(new_cls_def_context,
|
||||
self_type=custom_manager_type,
|
||||
new_method_name=name,
|
||||
method_node=sym.node)
|
||||
continue
|
||||
|
||||
new_sym = sym.copy()
|
||||
if isinstance(new_sym.node, Var):
|
||||
new_var = Var(name, type=sym.type)
|
||||
new_var.info = new_manager_info
|
||||
new_var._fullname = new_manager_info.fullname + '.' + name
|
||||
new_sym.node = new_var
|
||||
new_manager_info.names[name] = new_sym
|
||||
|
||||
return custom_manager_type
|
||||
|
||||
def run_with_model_cls(self, model_cls: Type[Model]) -> None:
|
||||
for manager_name, manager in model_cls._meta.managers_map.items():
|
||||
manager_class_name = manager.__class__.__name__
|
||||
manager_fullname = helpers.get_class_fullname(manager.__class__)
|
||||
manager_info = self.lookup_typeinfo_or_incomplete_defn_error(manager_fullname)
|
||||
try:
|
||||
manager_info = self.lookup_typeinfo_or_incomplete_defn_error(manager_fullname)
|
||||
except helpers.IncompleteDefnException as exc:
|
||||
if not self.api.final_iteration:
|
||||
raise exc
|
||||
else:
|
||||
base_manager_fullname = helpers.get_class_fullname(manager.__class__.__bases__[0])
|
||||
generated_managers = self.get_generated_manager_mappings(base_manager_fullname)
|
||||
if manager_fullname not in generated_managers:
|
||||
# not a generated manager, continue with the loop
|
||||
continue
|
||||
real_manager_fullname = generated_managers[manager_fullname]
|
||||
manager_info = self.lookup_typeinfo(real_manager_fullname) # type: ignore
|
||||
if manager_info is None:
|
||||
continue
|
||||
manager_class_name = real_manager_fullname.rsplit('.', maxsplit=1)[1]
|
||||
|
||||
if manager_name not in self.model_classdef.info.names:
|
||||
manager_type = Instance(manager_info, [Instance(self.model_classdef.info, [])])
|
||||
self.add_new_node_to_model_class(manager_name, manager_type)
|
||||
else:
|
||||
# creates new MODELNAME_MANAGERCLASSNAME class that represents manager parametrized with current model
|
||||
has_manager_any_base = any(self._is_manager_any(base) for base in manager_info.bases)
|
||||
if has_manager_any_base:
|
||||
custom_model_manager_name = manager.model.__name__ + '_' + manager.__class__.__name__
|
||||
if not self.has_any_parametrized_manager_as_base(manager_info):
|
||||
continue
|
||||
|
||||
bases = []
|
||||
for original_base in manager_info.bases:
|
||||
if self._is_manager_any(original_base):
|
||||
if original_base.type is None:
|
||||
raise helpers.IncompleteDefnException()
|
||||
custom_model_manager_name = manager.model.__name__ + '_' + manager_class_name
|
||||
try:
|
||||
custom_manager_type = self.create_new_model_parametrized_manager(custom_model_manager_name,
|
||||
base_manager_info=manager_info)
|
||||
except helpers.IncompleteDefnException:
|
||||
continue
|
||||
|
||||
original_base = helpers.reparametrize_instance(original_base,
|
||||
[Instance(self.model_classdef.info, [])])
|
||||
bases.append(original_base)
|
||||
|
||||
current_module = self.api.modules[self.model_classdef.info.module_name]
|
||||
custom_manager_info = helpers.add_new_class_for_module(current_module,
|
||||
custom_model_manager_name,
|
||||
bases=bases,
|
||||
fields=OrderedDict())
|
||||
# copy fields to a new manager
|
||||
new_cls_def_context = ClassDefContext(cls=custom_manager_info.defn,
|
||||
reason=self.ctx.reason,
|
||||
api=self.api)
|
||||
custom_manager_type = Instance(custom_manager_info, [Instance(self.model_classdef.info, [])])
|
||||
|
||||
for name, sym in manager_info.names.items():
|
||||
# replace self type with new class, if copying method
|
||||
if isinstance(sym.node, FuncDef):
|
||||
arguments, return_type = self.prepare_new_method_arguments(sym.node)
|
||||
add_method(new_cls_def_context,
|
||||
name,
|
||||
args=arguments,
|
||||
return_type=return_type,
|
||||
self_type=custom_manager_type)
|
||||
continue
|
||||
|
||||
new_sym = sym.copy()
|
||||
if isinstance(new_sym.node, Var):
|
||||
new_var = Var(name, type=sym.type)
|
||||
new_var.info = custom_manager_info
|
||||
new_var._fullname = custom_manager_info.fullname + '.' + name
|
||||
new_sym.node = new_var
|
||||
custom_manager_info.names[name] = new_sym
|
||||
|
||||
self.add_new_node_to_model_class(manager_name, custom_manager_type)
|
||||
|
||||
def prepare_new_method_arguments(self, node: FuncDef) -> Tuple[List[Argument], MypyType]:
|
||||
arguments = []
|
||||
for argument in node.arguments[1:]:
|
||||
if argument.type_annotation is None:
|
||||
argument.type_annotation = AnyType(TypeOfAny.unannotated)
|
||||
arguments.append(argument)
|
||||
|
||||
if isinstance(node.type, CallableType):
|
||||
return_type = node.type.ret_type
|
||||
else:
|
||||
return_type = AnyType(TypeOfAny.unannotated)
|
||||
|
||||
return arguments, return_type
|
||||
self.add_new_node_to_model_class(manager_name, custom_manager_type)
|
||||
|
||||
|
||||
class AddDefaultManagerAttribute(ModelClassInitializer):
|
||||
@@ -223,16 +258,44 @@ class AddRelatedManagers(ModelClassInitializer):
|
||||
if related_model_cls is None:
|
||||
continue
|
||||
|
||||
related_model_info = self.lookup_class_typeinfo_or_incomplete_defn_error(related_model_cls)
|
||||
try:
|
||||
related_model_info = self.lookup_class_typeinfo_or_incomplete_defn_error(related_model_cls)
|
||||
except helpers.IncompleteDefnException as exc:
|
||||
if not self.api.final_iteration:
|
||||
raise exc
|
||||
else:
|
||||
continue
|
||||
|
||||
if isinstance(relation, OneToOneRel):
|
||||
self.add_new_node_to_model_class(attname, Instance(related_model_info, []))
|
||||
continue
|
||||
|
||||
if isinstance(relation, (ManyToOneRel, ManyToManyRel)):
|
||||
manager_info = self.lookup_typeinfo_or_incomplete_defn_error(fullnames.RELATED_MANAGER_CLASS_FULLNAME)
|
||||
self.add_new_node_to_model_class(attname,
|
||||
Instance(manager_info, [Instance(related_model_info, [])]))
|
||||
continue
|
||||
try:
|
||||
related_manager_info = self.lookup_typeinfo_or_incomplete_defn_error(fullnames.RELATED_MANAGER_CLASS) # noqa: E501
|
||||
if 'objects' not in related_model_info.names:
|
||||
raise helpers.IncompleteDefnException()
|
||||
except helpers.IncompleteDefnException as exc:
|
||||
if not self.api.final_iteration:
|
||||
raise exc
|
||||
else:
|
||||
continue
|
||||
|
||||
# create new RelatedManager subclass
|
||||
parametrized_related_manager_type = Instance(related_manager_info,
|
||||
[Instance(related_model_info, [])])
|
||||
default_manager_type = related_model_info.names['objects'].type
|
||||
if (default_manager_type is None
|
||||
or not isinstance(default_manager_type, Instance)
|
||||
or default_manager_type.type.fullname == fullnames.MANAGER_CLASS_FULLNAME):
|
||||
self.add_new_node_to_model_class(attname, parametrized_related_manager_type)
|
||||
continue
|
||||
|
||||
name = related_model_cls.__name__ + '_' + 'RelatedManager'
|
||||
bases = [parametrized_related_manager_type, default_manager_type]
|
||||
new_related_manager_info = self.add_new_class_for_current_module(name, bases)
|
||||
|
||||
self.add_new_node_to_model_class(attname, Instance(new_related_manager_info, []))
|
||||
|
||||
|
||||
class AddExtraFieldMethods(ModelClassInitializer):
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
from collections import OrderedDict
|
||||
from typing import List, Optional, Sequence, Type, Union
|
||||
from typing import List, Optional, Sequence, Type
|
||||
|
||||
from django.core.exceptions import FieldError
|
||||
from django.db.models.base import Model
|
||||
from django.db.models.fields.related import RelatedField
|
||||
from django.db.models.fields.reverse_related import ForeignObjectRel
|
||||
from mypy.nodes import Expression, NameExpr
|
||||
from mypy.plugin import FunctionContext, MethodContext
|
||||
from mypy.types import AnyType, Instance
|
||||
@@ -47,7 +48,8 @@ def get_field_type_from_lookup(ctx: MethodContext, django_context: DjangoContext
|
||||
except LookupsAreUnsupported:
|
||||
return AnyType(TypeOfAny.explicit)
|
||||
|
||||
if isinstance(lookup_field, RelatedField) and lookup_field.column == lookup:
|
||||
if ((isinstance(lookup_field, RelatedField) and lookup_field.column == lookup)
|
||||
or isinstance(lookup_field, ForeignObjectRel)):
|
||||
related_model_cls = django_context.get_field_related_model_cls(lookup_field)
|
||||
if related_model_cls is None:
|
||||
return AnyType(TypeOfAny.from_error)
|
||||
@@ -60,7 +62,7 @@ def get_field_type_from_lookup(ctx: MethodContext, django_context: DjangoContext
|
||||
|
||||
def get_values_list_row_type(ctx: MethodContext, django_context: DjangoContext, model_cls: Type[Model],
|
||||
flat: bool, named: bool) -> MypyType:
|
||||
field_lookups = resolve_field_lookups(ctx.args[0], ctx, django_context)
|
||||
field_lookups = resolve_field_lookups(ctx.args[0], django_context)
|
||||
if field_lookups is None:
|
||||
return AnyType(TypeOfAny.from_error)
|
||||
|
||||
@@ -146,11 +148,10 @@ def extract_proper_type_queryset_values_list(ctx: MethodContext, django_context:
|
||||
return helpers.reparametrize_instance(ctx.default_return_type, [model_type, row_type])
|
||||
|
||||
|
||||
def resolve_field_lookups(lookup_exprs: Sequence[Expression], ctx: Union[FunctionContext, MethodContext],
|
||||
django_context: DjangoContext) -> Optional[List[str]]:
|
||||
def resolve_field_lookups(lookup_exprs: Sequence[Expression], django_context: DjangoContext) -> Optional[List[str]]:
|
||||
field_lookups = []
|
||||
for field_lookup_expr in lookup_exprs:
|
||||
field_lookup = helpers.resolve_string_attribute_value(field_lookup_expr, ctx, django_context)
|
||||
field_lookup = helpers.resolve_string_attribute_value(field_lookup_expr, django_context)
|
||||
if field_lookup is None:
|
||||
return None
|
||||
field_lookups.append(field_lookup)
|
||||
@@ -170,7 +171,7 @@ def extract_proper_type_queryset_values(ctx: MethodContext, django_context: Djan
|
||||
if model_cls is None:
|
||||
return ctx.default_return_type
|
||||
|
||||
field_lookups = resolve_field_lookups(ctx.args[0], ctx, django_context)
|
||||
field_lookups = resolve_field_lookups(ctx.args[0], django_context)
|
||||
if field_lookups is None:
|
||||
return AnyType(TypeOfAny.from_error)
|
||||
|
||||
|
||||
@@ -2,17 +2,17 @@
|
||||
# using this constant.
|
||||
import re
|
||||
|
||||
IGNORED_MODULES = {'schema', 'gis_tests', 'admin_widgets', 'admin_filters', 'migrations',
|
||||
'sitemaps_tests', 'staticfiles_tests', 'modeladmin', 'model_forms',
|
||||
'generic_views', 'forms_tests', 'flatpages_tests', 'admin_utils',
|
||||
'admin_ordering', 'admin_changelist', 'admin_views', 'redirects_tests',
|
||||
'invalid_models_tests', 'i18n', 'migrate_signals', 'model_formsets',
|
||||
IGNORED_MODULES = {'schema', 'gis_tests', 'admin_widgets', 'admin_filters',
|
||||
'sitemaps_tests', 'staticfiles_tests', 'modeladmin',
|
||||
'generic_views', 'forms_tests', 'flatpages_tests',
|
||||
'admin_ordering', 'admin_changelist', 'admin_views',
|
||||
'invalid_models_tests', 'i18n', 'model_formsets',
|
||||
'template_tests', 'template_backends', 'test_runner', 'admin_scripts',
|
||||
'sites_tests', 'inline_formsets', 'foreign_object', 'cache', 'test_client', 'test_client_regress'}
|
||||
'inline_formsets', 'foreign_object', 'cache'}
|
||||
|
||||
MOCK_OBJECTS = ['MockRequest', 'MockCompiler', 'modelz', 'call_count', 'call_args_list',
|
||||
MOCK_OBJECTS = ['MockRequest', 'MockCompiler', 'MockModelAdmin', 'modelz', 'call_count', 'call_args_list',
|
||||
'call_args', 'MockUser', 'Xtemplate', 'DummyRequest', 'DummyUser', 'MinimalUser', 'DummyNode']
|
||||
EXTERNAL_MODULES = ['psycopg2', 'PIL', 'selenium', 'oracle', 'mysql', 'sqlite3', 'sqlparse', 'tblib', 'numpy',
|
||||
EXTERNAL_MODULES = ['psycopg2', 'PIL', 'selenium', 'oracle', 'mysql', 'sqlparse', 'tblib', 'numpy',
|
||||
'bcrypt', 'argon2', 'xml.dom']
|
||||
IGNORED_ERRORS = {
|
||||
'__common__': [
|
||||
@@ -31,7 +31,6 @@ IGNORED_ERRORS = {
|
||||
re.compile(r'has no attribute ("|\')_[a-zA-Z_]+("|\')'),
|
||||
"'Settings' object has no attribute",
|
||||
'**Dict',
|
||||
re.compile(r"Expression of type '.*' is not supported"),
|
||||
'has incompatible type "object"',
|
||||
'undefined in superclass',
|
||||
'Argument after ** must be a mapping',
|
||||
@@ -65,6 +64,15 @@ IGNORED_ERRORS = {
|
||||
'Incompatible types in string interpolation',
|
||||
'"None" has no attribute',
|
||||
'has no attribute "assert',
|
||||
'Unsupported dynamic base class',
|
||||
'error: "HttpResponse" has no attribute "streaming_content"',
|
||||
'error: "HttpResponse" has no attribute "context_data"',
|
||||
],
|
||||
'admin_inlines': [
|
||||
'error: "HttpResponse" has no attribute "rendered_content"',
|
||||
],
|
||||
'admin_utils': [
|
||||
'"Article" has no attribute "non_field"',
|
||||
],
|
||||
'aggregation': [
|
||||
re.compile(r'got "Optional\[(Author|Publisher)\]", expected "Union\[(Author|Publisher), Combinable\]"'),
|
||||
@@ -101,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]"',
|
||||
@@ -130,7 +139,6 @@ IGNORED_ERRORS = {
|
||||
'"Employee" has no attribute "id"',
|
||||
],
|
||||
'custom_managers': [
|
||||
'Unsupported dynamic base class',
|
||||
'Incompatible types in assignment (expression has type "CharField',
|
||||
'Item "Book" of "Optional[Book]" has no attribute "favorite_avg"',
|
||||
],
|
||||
@@ -162,7 +170,8 @@ IGNORED_ERRORS = {
|
||||
'Incompatible types in assignment (expression has type "Optional[Any]", variable has type "FloatModel")'
|
||||
],
|
||||
'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"'
|
||||
@@ -176,7 +185,6 @@ IGNORED_ERRORS = {
|
||||
],
|
||||
'files': [
|
||||
'Incompatible types in assignment (expression has type "IOBase", variable has type "File")',
|
||||
'Argument 1 to "write" of "SpooledTemporaryFile"',
|
||||
],
|
||||
'filtered_relation': [
|
||||
'has no attribute "name"',
|
||||
@@ -190,7 +198,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"; '
|
||||
@@ -229,13 +237,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"',
|
||||
@@ -244,9 +247,10 @@ 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 "RelatedManager[Article]"',
|
||||
'(expression has type "List[Article]", variable has type "Article_RelatedManager2',
|
||||
'"add" of "RelatedManager" has incompatible type "Article"; expected "Union[Publication, int]"',
|
||||
],
|
||||
'many_to_one': [
|
||||
@@ -259,6 +263,20 @@ IGNORED_ERRORS = {
|
||||
'middleware_exceptions': [
|
||||
'Argument 1 to "append" of "list" has incompatible type "Tuple[Any, Any]"; expected "str"'
|
||||
],
|
||||
'migrate_signals': [
|
||||
'Value of type "Optional[Any]" is not indexable',
|
||||
'Argument 1 to "set" has incompatible type "Optional[Any]"; expected "Iterable[Any]"',
|
||||
],
|
||||
'migrations': [
|
||||
'FakeMigration',
|
||||
'FakeLoader',
|
||||
'"Manager[Any]" has no attribute "args"',
|
||||
'Dict entry 0 has incompatible type "Any"',
|
||||
'Argument 1 to "append" of "list" has incompatible type',
|
||||
'base class "Model" defined the type as "BaseManager[Any]"',
|
||||
'Argument 1 to "RunPython" has incompatible type "str"',
|
||||
|
||||
],
|
||||
'model_fields': [
|
||||
'Item "Field[Any, Any]" of "Union[Field[Any, Any], ForeignObjectRel]" has no attribute',
|
||||
'Incompatible types in assignment (expression has type "Type[Person',
|
||||
@@ -266,6 +284,15 @@ 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"',
|
||||
"Module 'django.core.validators' has no attribute 'ValidationError'",
|
||||
'Incompatible types in assignment',
|
||||
'NewForm',
|
||||
'"type" has no attribute "base_fields"',
|
||||
'Argument "instance" to "InvalidModelForm" has incompatible type "Type[Category]"',
|
||||
],
|
||||
'model_indexes': [
|
||||
'Argument "condition" to "Index" has incompatible type "str"; expected "Optional[Q]"'
|
||||
@@ -296,9 +323,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"
|
||||
],
|
||||
@@ -361,6 +385,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]";'
|
||||
],
|
||||
@@ -368,8 +395,11 @@ IGNORED_ERRORS = {
|
||||
'expression has type "CurrentSiteManager[CustomArticle]", base class "AbstractArticle"',
|
||||
"Name 'Optional' is not defined",
|
||||
],
|
||||
'sites_tests': [
|
||||
'"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")',
|
||||
@@ -404,6 +434,14 @@ IGNORED_ERRORS = {
|
||||
'"PossessedCar" has no attribute "color"',
|
||||
'expression has type "None", variable has type "List[str]"',
|
||||
],
|
||||
'test_client': [
|
||||
'(expression has type "HttpResponse", variable has type "StreamingHttpResponse")'
|
||||
],
|
||||
'test_client_regress': [
|
||||
'(expression has type "Dict[<nothing>, <nothing>]", variable has type "SessionBase")',
|
||||
'Unsupported left operand type for + ("None")',
|
||||
'Argument 1 to "len" has incompatible type "Context"; expected "Sized"',
|
||||
],
|
||||
'transactions': [
|
||||
'Incompatible types in assignment (expression has type "Thread", variable has type "Callable[[], Any]")'
|
||||
],
|
||||
@@ -432,7 +470,7 @@ IGNORED_ERRORS = {
|
||||
'No overload variant of "join" matches argument types "str", "None"',
|
||||
'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'",
|
||||
|
||||
@@ -5,17 +5,17 @@ import sys
|
||||
from argparse import ArgumentParser
|
||||
from collections import defaultdict
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Pattern, Union
|
||||
from typing import Dict, List, Pattern, Tuple, Union
|
||||
|
||||
from git import Repo
|
||||
from git import RemoteProgress, Repo
|
||||
|
||||
from scripts.enabled_test_modules import (
|
||||
EXTERNAL_MODULES, IGNORED_ERRORS, IGNORED_MODULES, MOCK_OBJECTS,
|
||||
)
|
||||
|
||||
DJANGO_COMMIT_REFS = {
|
||||
'2.2': 'e8b0903976077b951795938b260211214ed7fe41',
|
||||
'3.0': '7ec5962638144cbf4c2e47ea7d8dc02d1ce44394'
|
||||
DJANGO_COMMIT_REFS: Dict[str, Tuple[str, str]] = {
|
||||
'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
|
||||
@@ -75,10 +75,21 @@ def replace_with_clickable_location(error: str, abs_test_folder: Path) -> str:
|
||||
return error.replace(raw_path, clickable_location)
|
||||
|
||||
|
||||
def get_django_repo_object() -> Repo:
|
||||
class ProgressPrinter(RemoteProgress):
|
||||
def line_dropped(self, line: str) -> None:
|
||||
print(line)
|
||||
|
||||
def update(self, op_code, cur_count, max_count=None, message=''):
|
||||
print(self._cur_line)
|
||||
|
||||
|
||||
def get_django_repo_object(branch: str) -> Repo:
|
||||
if not DJANGO_SOURCE_DIRECTORY.exists():
|
||||
DJANGO_SOURCE_DIRECTORY.mkdir(exist_ok=True, parents=False)
|
||||
return Repo.clone_from('https://github.com/django/django.git', DJANGO_SOURCE_DIRECTORY)
|
||||
return Repo.clone_from('https://github.com/django/django.git', DJANGO_SOURCE_DIRECTORY,
|
||||
progress=ProgressPrinter(),
|
||||
branch=branch,
|
||||
depth=100)
|
||||
else:
|
||||
repo = Repo(DJANGO_SOURCE_DIRECTORY)
|
||||
return repo
|
||||
@@ -89,10 +100,13 @@ if __name__ == '__main__':
|
||||
parser.add_argument('--django_version', choices=['2.2', '3.0'], required=True)
|
||||
args = parser.parse_args()
|
||||
|
||||
commit_sha = DJANGO_COMMIT_REFS[args.django_version]
|
||||
repo = get_django_repo_object()
|
||||
# install proper Django version
|
||||
subprocess.check_call([sys.executable, '-m', 'pip', 'install', f'Django=={args.django_version}.*'])
|
||||
|
||||
branch, commit_sha = DJANGO_COMMIT_REFS[args.django_version]
|
||||
repo = get_django_repo_object(branch)
|
||||
if repo.head.commit.hexsha != commit_sha:
|
||||
repo.git.fetch('origin')
|
||||
repo.remote('origin').fetch(branch, progress=ProgressPrinter(), depth=100)
|
||||
repo.git.checkout(commit_sha)
|
||||
|
||||
mypy_config_file = (PROJECT_DIRECTORY / 'scripts' / 'mypy.ini').absolute()
|
||||
|
||||
4
setup.py
4
setup.py
@@ -21,14 +21,14 @@ with open('README.md', 'r') as f:
|
||||
readme = f.read()
|
||||
|
||||
dependencies = [
|
||||
'mypy>=0.750,<0.760',
|
||||
'mypy>=0.770,<0.780',
|
||||
'typing-extensions',
|
||||
'django',
|
||||
]
|
||||
|
||||
setup(
|
||||
name="django-stubs",
|
||||
version="1.3.0",
|
||||
version="1.5.0",
|
||||
description='Mypy stubs for Django',
|
||||
long_description=readme,
|
||||
long_description_content_type='text/markdown',
|
||||
|
||||
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): ...
|
||||
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'
|
||||
|
||||
@@ -648,3 +648,27 @@
|
||||
abstract = True
|
||||
class User(AbstractUser):
|
||||
pass
|
||||
|
||||
|
||||
- case: related_manager_is_a_subclass_of_default_manager
|
||||
main: |
|
||||
from myapp.models import User
|
||||
reveal_type(User().orders) # N: Revealed type is 'myapp.models.Order_RelatedManager'
|
||||
reveal_type(User().orders.get()) # N: Revealed type is 'myapp.models.Order*'
|
||||
reveal_type(User().orders.manager_method()) # N: Revealed type is 'builtins.int'
|
||||
installed_apps:
|
||||
- myapp
|
||||
files:
|
||||
- path: myapp/__init__.py
|
||||
- path: myapp/models.py
|
||||
content: |
|
||||
from django.db import models
|
||||
class User(models.Model):
|
||||
pass
|
||||
class OrderManager(models.Manager):
|
||||
def manager_method(self) -> int:
|
||||
pass
|
||||
class Order(models.Model):
|
||||
objects = OrderManager()
|
||||
user = models.ForeignKey(to=User, on_delete=models.CASCADE, related_name='orders')
|
||||
|
||||
|
||||
181
test-data/typecheck/managers/querysets/test_from_queryset.yml
Normal file
181
test-data/typecheck/managers/querysets/test_from_queryset.yml
Normal file
@@ -0,0 +1,181 @@
|
||||
- case: from_queryset_with_base_manager
|
||||
main: |
|
||||
from myapp.models import MyModel
|
||||
reveal_type(MyModel().objects) # N: Revealed type is 'myapp.models.MyModel_NewManager[myapp.models.MyModel]'
|
||||
reveal_type(MyModel().objects.get()) # N: Revealed type is 'myapp.models.MyModel*'
|
||||
reveal_type(MyModel().objects.queryset_method()) # N: Revealed type is 'builtins.str'
|
||||
installed_apps:
|
||||
- myapp
|
||||
files:
|
||||
- path: myapp/__init__.py
|
||||
- path: myapp/models.py
|
||||
content: |
|
||||
from django.db import models
|
||||
from django.db.models.manager import BaseManager
|
||||
|
||||
class ModelQuerySet(models.QuerySet):
|
||||
def queryset_method(self) -> str:
|
||||
return 'hello'
|
||||
NewManager = BaseManager.from_queryset(ModelQuerySet)
|
||||
class MyModel(models.Model):
|
||||
objects = NewManager()
|
||||
|
||||
- case: from_queryset_with_manager
|
||||
main: |
|
||||
from myapp.models import MyModel
|
||||
reveal_type(MyModel().objects) # N: Revealed type is 'myapp.models.MyModel_NewManager[myapp.models.MyModel]'
|
||||
reveal_type(MyModel().objects.get()) # N: Revealed type is 'myapp.models.MyModel*'
|
||||
reveal_type(MyModel().objects.queryset_method()) # N: Revealed type is 'builtins.str'
|
||||
installed_apps:
|
||||
- myapp
|
||||
files:
|
||||
- path: myapp/__init__.py
|
||||
- path: myapp/models.py
|
||||
content: |
|
||||
from django.db import models
|
||||
|
||||
class ModelQuerySet(models.QuerySet):
|
||||
def queryset_method(self) -> str:
|
||||
return 'hello'
|
||||
|
||||
NewManager = models.Manager.from_queryset(ModelQuerySet)
|
||||
class MyModel(models.Model):
|
||||
objects = NewManager()
|
||||
|
||||
- case: from_queryset_returns_intersection_of_manager_and_queryset
|
||||
main: |
|
||||
from myapp.models import MyModel, NewManager
|
||||
reveal_type(NewManager()) # N: Revealed type is 'myapp.models.NewManager'
|
||||
reveal_type(MyModel.objects) # N: Revealed type is 'myapp.models.MyModel_NewManager[myapp.models.MyModel]'
|
||||
reveal_type(MyModel.objects.get()) # N: Revealed type is 'Any'
|
||||
reveal_type(MyModel.objects.manager_only_method()) # N: Revealed type is 'builtins.int'
|
||||
reveal_type(MyModel.objects.manager_and_queryset_method()) # N: Revealed type is 'builtins.str'
|
||||
installed_apps:
|
||||
- myapp
|
||||
files:
|
||||
- path: myapp/__init__.py
|
||||
- path: myapp/models.py
|
||||
content: |
|
||||
from django.db import models
|
||||
class ModelBaseManager(models.Manager):
|
||||
def manager_only_method(self) -> int:
|
||||
return 1
|
||||
class ModelQuerySet(models.QuerySet):
|
||||
def manager_and_queryset_method(self) -> str:
|
||||
return 'hello'
|
||||
|
||||
NewManager = ModelBaseManager.from_queryset(ModelQuerySet)
|
||||
class MyModel(models.Model):
|
||||
objects = NewManager()
|
||||
|
||||
- case: from_queryset_with_class_name_provided
|
||||
main: |
|
||||
from myapp.models import MyModel, NewManager
|
||||
reveal_type(NewManager()) # N: Revealed type is 'myapp.models.NewManager'
|
||||
reveal_type(MyModel.objects) # N: Revealed type is 'myapp.models.MyModel_NewManager[myapp.models.MyModel]'
|
||||
reveal_type(MyModel.objects.get()) # N: Revealed type is 'Any'
|
||||
reveal_type(MyModel.objects.manager_only_method()) # N: Revealed type is 'builtins.int'
|
||||
reveal_type(MyModel.objects.manager_and_queryset_method()) # N: Revealed type is 'builtins.str'
|
||||
installed_apps:
|
||||
- myapp
|
||||
files:
|
||||
- path: myapp/__init__.py
|
||||
- path: myapp/models.py
|
||||
content: |
|
||||
from django.db import models
|
||||
class ModelBaseManager(models.Manager):
|
||||
def manager_only_method(self) -> int:
|
||||
return 1
|
||||
class ModelQuerySet(models.QuerySet):
|
||||
def manager_and_queryset_method(self) -> str:
|
||||
return 'hello'
|
||||
|
||||
NewManager = ModelBaseManager.from_queryset(ModelQuerySet, class_name='NewManager')
|
||||
class MyModel(models.Model):
|
||||
objects = NewManager()
|
||||
|
||||
- case: from_queryset_with_class_inheritance
|
||||
main: |
|
||||
from myapp.models import MyModel
|
||||
reveal_type(MyModel().objects) # N: Revealed type is 'myapp.models.MyModel_NewManager[myapp.models.MyModel]'
|
||||
reveal_type(MyModel().objects.get()) # N: Revealed type is 'myapp.models.MyModel*'
|
||||
reveal_type(MyModel().objects.queryset_method()) # N: Revealed type is 'builtins.str'
|
||||
installed_apps:
|
||||
- myapp
|
||||
files:
|
||||
- path: myapp/__init__.py
|
||||
- path: myapp/models.py
|
||||
content: |
|
||||
from django.db import models
|
||||
from django.db.models.manager import BaseManager
|
||||
class BaseQuerySet(models.QuerySet):
|
||||
def queryset_method(self) -> str:
|
||||
return 'hello'
|
||||
class ModelQuerySet(BaseQuerySet):
|
||||
pass
|
||||
|
||||
NewManager = BaseManager.from_queryset(ModelQuerySet)
|
||||
class MyModel(models.Model):
|
||||
objects = NewManager()
|
||||
|
||||
- case: from_queryset_with_manager_in_another_directory_and_imports
|
||||
main: |
|
||||
from myapp.models import MyModel
|
||||
reveal_type(MyModel().objects) # N: Revealed type is 'myapp.models.MyModel_NewManager[myapp.models.MyModel]'
|
||||
reveal_type(MyModel().objects.get()) # N: Revealed type is 'myapp.models.MyModel*'
|
||||
reveal_type(MyModel().objects.queryset_method) # N: Revealed type is 'def (param: Union[builtins.str, None] =) -> Union[builtins.str, None]'
|
||||
reveal_type(MyModel().objects.queryset_method('str')) # N: Revealed type is 'Union[builtins.str, None]'
|
||||
installed_apps:
|
||||
- myapp
|
||||
files:
|
||||
- path: myapp/__init__.py
|
||||
- path: myapp/models.py
|
||||
content: |
|
||||
from django.db import models
|
||||
from myapp.managers import NewManager
|
||||
|
||||
class MyModel(models.Model):
|
||||
objects = NewManager()
|
||||
- path: myapp/managers.py
|
||||
content: |
|
||||
from typing import Optional
|
||||
from django.db import models
|
||||
|
||||
class ModelQuerySet(models.QuerySet):
|
||||
def queryset_method(self, param: Optional[str] = None) -> Optional[str]:
|
||||
return param
|
||||
|
||||
NewManager = models.Manager.from_queryset(ModelQuerySet)
|
||||
|
||||
- case: from_queryset_with_inherited_manager_and_typing_no_return
|
||||
disable_cache: true
|
||||
main: |
|
||||
from myapp.models import MyModel
|
||||
reveal_type(MyModel().objects) # N: Revealed type is 'myapp.models.MyModel_NewManager[myapp.models.MyModel]'
|
||||
reveal_type(MyModel().objects.get()) # N: Revealed type is 'myapp.models.MyModel*'
|
||||
reveal_type(MyModel().objects.base_queryset_method) # N: Revealed type is 'def (param: Union[builtins.int, builtins.str]) -> <nothing>'
|
||||
reveal_type(MyModel().objects.base_queryset_method(2)) # N: Revealed type is '<nothing>'
|
||||
installed_apps:
|
||||
- myapp
|
||||
files:
|
||||
- path: myapp/__init__.py
|
||||
- path: myapp/models.py
|
||||
content: |
|
||||
from django.db import models
|
||||
from myapp.managers import NewManager
|
||||
class MyModel(models.Model):
|
||||
objects = NewManager()
|
||||
- path: myapp/managers.py
|
||||
content: |
|
||||
from django.db import models
|
||||
from myapp.base_queryset import BaseQuerySet
|
||||
class ModelQuerySet(BaseQuerySet):
|
||||
pass
|
||||
NewManager = models.Manager.from_queryset(ModelQuerySet)
|
||||
- path: myapp/base_queryset.py
|
||||
content: |
|
||||
from typing import NoReturn, Union
|
||||
from django.db import models
|
||||
class BaseQuerySet(models.QuerySet):
|
||||
def base_queryset_method(self, param: Union[int, str]) -> NoReturn:
|
||||
raise ValueError
|
||||
@@ -107,3 +107,20 @@
|
||||
class Blog(models.Model):
|
||||
name = models.CharField(max_length=100)
|
||||
publisher = models.ForeignKey(Publisher, on_delete=models.CASCADE)
|
||||
|
||||
- case: values_of_many_to_many_field
|
||||
main: |
|
||||
from myapp.models import Author, Book
|
||||
reveal_type(Book.objects.values('authors')) # N: Revealed type is 'django.db.models.query.ValuesQuerySet[myapp.models.Book, TypedDict({'authors': builtins.int})]'
|
||||
reveal_type(Author.objects.values('books')) # N: Revealed type is 'django.db.models.query.ValuesQuerySet[myapp.models.Author, TypedDict({'books': builtins.int})]'
|
||||
installed_apps:
|
||||
- myapp
|
||||
files:
|
||||
- path: myapp/__init__.py
|
||||
- path: myapp/models.py
|
||||
content: |
|
||||
from django.db import models
|
||||
class Author(models.Model):
|
||||
pass
|
||||
class Book(models.Model):
|
||||
authors = models.ManyToManyField(Author, related_name='books')
|
||||
|
||||
@@ -224,3 +224,20 @@
|
||||
pass
|
||||
class Transaction(models.Model):
|
||||
total = models.IntegerField()
|
||||
|
||||
- case: values_list_of_many_to_many_field
|
||||
main: |
|
||||
from myapp.models import Author, Book
|
||||
reveal_type(Book.objects.values_list('authors')) # N: Revealed type is 'django.db.models.query.ValuesQuerySet[myapp.models.Book, Tuple[builtins.int]]'
|
||||
reveal_type(Author.objects.values_list('books')) # N: Revealed type is 'django.db.models.query.ValuesQuerySet[myapp.models.Author, Tuple[builtins.int]]'
|
||||
installed_apps:
|
||||
- myapp
|
||||
files:
|
||||
- path: myapp/__init__.py
|
||||
- path: myapp/models.py
|
||||
content: |
|
||||
from django.db import models
|
||||
class Author(models.Model):
|
||||
pass
|
||||
class Book(models.Model):
|
||||
authors = models.ManyToManyField(Author, related_name='books')
|
||||
@@ -48,7 +48,7 @@
|
||||
class Base(Generic[_T]):
|
||||
def __init__(self, model_cls: Type[_T]):
|
||||
self.model_cls = model_cls
|
||||
reveal_type(self.model_cls._default_manager) # N: Revealed type is 'django.db.models.manager.Manager[django.db.models.base.Model]'
|
||||
reveal_type(self.model_cls._default_manager) # N: Revealed type is 'django.db.models.manager.BaseManager[django.db.models.base.Model]'
|
||||
class MyModel(models.Model):
|
||||
pass
|
||||
class Child(Base[MyModel]):
|
||||
@@ -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'
|
||||
|
||||
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'
|
||||
@@ -96,6 +96,7 @@
|
||||
import django.contrib.postgres.aggregates.general
|
||||
import django.contrib.postgres.aggregates.mixins
|
||||
import django.contrib.postgres.aggregates.statistics
|
||||
import django.contrib.postgres.constraints
|
||||
import django.contrib.postgres.fields
|
||||
import django.contrib.postgres.fields.array
|
||||
import django.contrib.postgres.fields.citext
|
||||
@@ -269,6 +270,7 @@
|
||||
import django.db.models.constraints
|
||||
import django.db.models.deletion
|
||||
import django.db.models.expressions
|
||||
import django.db.models.enums
|
||||
import django.db.models.fields
|
||||
import django.db.models.fields.files
|
||||
import django.db.models.fields.mixins
|
||||
|
||||
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'
|
||||
18
test-data/typecheck/utils/test_functional.yml
Normal file
18
test-data/typecheck/utils/test_functional.yml
Normal 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"
|
||||
Reference in New Issue
Block a user