mirror of
https://github.com/davidhalter/django-stubs.git
synced 2025-12-10 22:11:54 +08:00
Compare commits
55 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
28afe59881 | ||
|
|
54e5ecc16a | ||
|
|
418437b00c | ||
|
|
eb195b61e9 | ||
|
|
993dd816a4 | ||
|
|
e6dcfe4fc6 | ||
|
|
8f9e77ce39 | ||
|
|
a1334a70b9 | ||
|
|
a8d8561d0e | ||
|
|
bdc7db1154 | ||
|
|
49ed9c957f | ||
|
|
4829ce2200 | ||
|
|
9fe1a76779 | ||
|
|
153613eddb | ||
|
|
b6cb50e729 | ||
|
|
344dea06b5 | ||
|
|
01a9ad12e1 | ||
|
|
0a0e1985d7 | ||
|
|
f07477260f | ||
|
|
11c3f8cd11 | ||
|
|
159f8b95ba | ||
|
|
58c087f7f5 | ||
|
|
2e9adce5eb | ||
|
|
47bd07758b | ||
|
|
36d1778e2a | ||
|
|
caaa23ab8f | ||
|
|
cfd9379b58 | ||
|
|
362342d998 | ||
|
|
d3ff5415db | ||
|
|
eecf13a2fe | ||
|
|
d9c851abce | ||
|
|
896cbe4752 | ||
|
|
f3e0872d6e | ||
|
|
4cb13a6ac5 | ||
|
|
19d695b1da | ||
|
|
16326e999c | ||
|
|
517ae648e5 | ||
|
|
3e0f144148 | ||
|
|
8a68111ce5 | ||
|
|
9f966a8056 | ||
|
|
0c41d0c6e9 | ||
|
|
e798b496c0 | ||
|
|
01af2ff588 | ||
|
|
aab8acf2ea | ||
|
|
1c4a7d25c7 | ||
|
|
a0d61c0de3 | ||
|
|
e837dac26a | ||
|
|
bbdf15a6ec | ||
|
|
f08b428027 | ||
|
|
44151c485d | ||
|
|
a3624dec36 | ||
|
|
9a9a0123c7 | ||
|
|
d3d854dac8 | ||
|
|
6dc2c32382 | ||
|
|
695cdb16ca |
20
.github/workflows/misspel.yml
vendored
Normal file
20
.github/workflows/misspel.yml
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
name: misspell
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
schedule:
|
||||||
|
- cron: '0 0 * * *'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: sobolevn/misspell-fixer-action@0.1.0
|
||||||
|
- uses: peter-evans/create-pull-request@v3
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
commit-message: 'Fixes by misspell-fixer'
|
||||||
|
title: 'Typos fix by misspell-fixer'
|
||||||
85
.github/workflows/test.yml
vendored
Normal file
85
.github/workflows/test.yml
vendored
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
name: test
|
||||||
|
|
||||||
|
on: [push, pull_request, workflow_dispatch]
|
||||||
|
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
lint:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
python-version: ['3.9']
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Set up Python ${{ matrix.python-version }}
|
||||||
|
uses: actions/setup-python@v2
|
||||||
|
with:
|
||||||
|
python-version: ${{ matrix.python-version }}
|
||||||
|
- uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
path: ~/.cache/pip
|
||||||
|
key: ${{ runner.os }}-pip-${{ matrix.python-version }}-lint-${{ hashFiles('./dev-requirements.txt') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-pip-${{ matrix.python-version }}-lint-
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
pip install -U pip setuptools wheel
|
||||||
|
pip install -r ./dev-requirements.txt
|
||||||
|
|
||||||
|
- name: Run pre-commit
|
||||||
|
run: pre-commit install && pre-commit run --all-files
|
||||||
|
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
python-version: ['3.7']
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Setup system dependencies
|
||||||
|
run: sudo apt-get install binutils libproj-dev gdal-bin
|
||||||
|
- name: Set up Python ${{ matrix.python-version }}
|
||||||
|
uses: actions/setup-python@v2
|
||||||
|
with:
|
||||||
|
python-version: ${{ matrix.python-version }}
|
||||||
|
- uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
path: ~/.cache/pip
|
||||||
|
key: ${{ runner.os }}-pip-${{ matrix.python-version }}-test-${{ hashFiles('./dev-requirements.txt') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-pip-${{ matrix.python-version }}-test-
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
pip install -U pip setuptools wheel
|
||||||
|
pip install -r ./dev-requirements.txt
|
||||||
|
|
||||||
|
- name: Run tests
|
||||||
|
run: pytest
|
||||||
|
|
||||||
|
typecheck:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
python-version: ['3.6', '3.7', '3.8', '3.9']
|
||||||
|
django-version: ['2.2', '3.0']
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Setup system dependencies
|
||||||
|
run: sudo apt-get install binutils libproj-dev gdal-bin
|
||||||
|
- name: Set up Python ${{ matrix.python-version }}
|
||||||
|
uses: actions/setup-python@v2
|
||||||
|
with:
|
||||||
|
python-version: ${{ matrix.python-version }}
|
||||||
|
- uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
path: ~/.cache/pip
|
||||||
|
key: ${{ runner.os }}-pip-${{ matrix.python-version }}-${{ matrix.django-version }}-typecheck-${{ hashFiles('./dev-requirements.txt') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-pip-${{ matrix.python-version }}-${{ matrix.django-version }}-typecheck-
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
pip install -U pip setuptools wheel
|
||||||
|
pip install -r ./dev-requirements.txt
|
||||||
|
|
||||||
|
- name: Run tests
|
||||||
|
run: python ./scripts/typecheck_tests.py --django_version="${{ matrix.django-version }}"
|
||||||
17
.gitignore
vendored
17
.gitignore
vendored
@@ -1,15 +1,12 @@
|
|||||||
*.egg-info
|
*.egg-info
|
||||||
__pycache__/
|
.DS_Store
|
||||||
out/
|
|
||||||
/test_sqlite.py
|
|
||||||
/django
|
|
||||||
.idea/
|
.idea/
|
||||||
.mypy_cache/
|
.mypy_cache/
|
||||||
build/
|
|
||||||
dist/
|
|
||||||
pip-wheel-metadata/
|
|
||||||
.pytest_cache/
|
.pytest_cache/
|
||||||
/.envrc
|
|
||||||
/.direnv
|
|
||||||
django-sources/
|
|
||||||
.venv/
|
.venv/
|
||||||
|
__pycache__/
|
||||||
|
django-source/
|
||||||
|
out/
|
||||||
|
pip-wheel-metadata/
|
||||||
|
stubgen/
|
||||||
|
build/
|
||||||
|
|||||||
46
.pre-commit-config.yaml
Normal file
46
.pre-commit-config.yaml
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
# See https://pre-commit.com for more information
|
||||||
|
# See https://pre-commit.com/hooks.html for more hooks
|
||||||
|
default_language_version:
|
||||||
|
python: python3.9
|
||||||
|
repos:
|
||||||
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
|
rev: v3.3.0
|
||||||
|
hooks:
|
||||||
|
- id: check-yaml
|
||||||
|
- id: trailing-whitespace
|
||||||
|
- id: check-executables-have-shebangs
|
||||||
|
- id: debug-statements
|
||||||
|
- id: check-merge-conflict
|
||||||
|
- repo: https://github.com/asottile/pyupgrade
|
||||||
|
rev: v2.7.3
|
||||||
|
hooks:
|
||||||
|
- id: pyupgrade
|
||||||
|
args: ["--py36-plus"]
|
||||||
|
- repo: https://github.com/pre-commit/mirrors-isort
|
||||||
|
rev: v5.6.4
|
||||||
|
hooks:
|
||||||
|
- id: isort
|
||||||
|
- repo: https://github.com/psf/black
|
||||||
|
rev: 20.8b1
|
||||||
|
hooks:
|
||||||
|
- id: black
|
||||||
|
- repo: https://gitlab.com/pycqa/flake8
|
||||||
|
rev: 3.8.4
|
||||||
|
hooks:
|
||||||
|
- id: flake8
|
||||||
|
- repo: local
|
||||||
|
hooks:
|
||||||
|
- id: mypy
|
||||||
|
name: mypy
|
||||||
|
entry: mypy
|
||||||
|
language: system
|
||||||
|
types: [ python ]
|
||||||
|
exclude: "scripts/|django_stubs_ext/"
|
||||||
|
args: [ "--config=mypy.ini", "--cache-dir=/dev/null", "--no-incremental" ]
|
||||||
|
- id: mypy
|
||||||
|
name: mypy (django_stubs_ext)
|
||||||
|
entry: mypy
|
||||||
|
language: system
|
||||||
|
types: [ python ]
|
||||||
|
files: "django_stubs_ext/|django_stubs_ext/tests/"
|
||||||
|
args: [ "--config=mypy.ini", "--cache-dir=/dev/null", "--no-incremental", "--strict" ]
|
||||||
61
.travis.yml
61
.travis.yml
@@ -1,61 +0,0 @@
|
|||||||
language: python
|
|
||||||
cache: pip
|
|
||||||
dist: xenial
|
|
||||||
sudo: required
|
|
||||||
jobs:
|
|
||||||
include:
|
|
||||||
- name: Run plugin test suite with python 3.8
|
|
||||||
python: 3.8
|
|
||||||
script: 'pytest'
|
|
||||||
|
|
||||||
- name: Run plugin test suite with python 3.7
|
|
||||||
python: 3.7
|
|
||||||
script: 'pytest'
|
|
||||||
|
|
||||||
- name: Typecheck Django 3.0 test suite with python 3.8
|
|
||||||
python: 3.8
|
|
||||||
script: |
|
|
||||||
python ./scripts/typecheck_tests.py --django_version=3.0
|
|
||||||
|
|
||||||
- name: Typecheck Django 3.0 test suite with python 3.7
|
|
||||||
python: 3.7
|
|
||||||
script: |
|
|
||||||
python ./scripts/typecheck_tests.py --django_version=3.0
|
|
||||||
|
|
||||||
- name: Typecheck Django 3.0 test suite with python 3.6
|
|
||||||
python: 3.6
|
|
||||||
script: |
|
|
||||||
python ./scripts/typecheck_tests.py --django_version=3.0
|
|
||||||
|
|
||||||
- name: Typecheck Django 2.2 test suite with python 3.7
|
|
||||||
python: 3.7
|
|
||||||
script: |
|
|
||||||
python ./scripts/typecheck_tests.py --django_version=2.2
|
|
||||||
|
|
||||||
- name: Mypy for plugin code
|
|
||||||
python: 3.7
|
|
||||||
script: 'mypy ./mypy_django_plugin'
|
|
||||||
|
|
||||||
- name: Lint with black
|
|
||||||
python: 3.7
|
|
||||||
script: 'black --check django-stubs/ setup.py'
|
|
||||||
|
|
||||||
- name: Lint plugin code with flake8
|
|
||||||
python: 3.7
|
|
||||||
script: 'flake8'
|
|
||||||
|
|
||||||
- name: Lint stubs with flake8-pyi and check for unused imports
|
|
||||||
python: 3.7
|
|
||||||
script: 'flake8 --config flake8-pyi.ini'
|
|
||||||
|
|
||||||
- name: Lint plugin code with isort
|
|
||||||
python: 3.7
|
|
||||||
script: 'isort --check --diff'
|
|
||||||
|
|
||||||
before_install: |
|
|
||||||
sudo add-apt-repository ppa:ubuntugis/ubuntugis-unstable -y
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install -y binutils libproj-dev gdal-bin
|
|
||||||
pip install -U pip setuptools wheel
|
|
||||||
install: |
|
|
||||||
pip install -r ./dev-requirements.txt
|
|
||||||
137
CONTRIBUTING.md
137
CONTRIBUTING.md
@@ -1,111 +1,112 @@
|
|||||||
# How to contribute
|
# Contribution Guide
|
||||||
|
|
||||||
|
This project is open source and community driven. As such we encourage code contributions of all kinds. Some areas you can contribute in:
|
||||||
|
|
||||||
|
1. Improve the stubs
|
||||||
|
2. Sync stubs with the latest version of Django
|
||||||
|
3. Improve plugin code and extend its capabilities
|
||||||
|
4. Write tests
|
||||||
|
5. Update dependencies
|
||||||
|
|
||||||
## Tutorials
|
## Tutorials
|
||||||
|
|
||||||
If you want to start working on this project,
|
If you want to start working on this project, you will need to get familiar with python typings.
|
||||||
you will need to get familiar with these projects:
|
The Mypy documentation offers an excellent resource for this, as well as the python official documentation:
|
||||||
|
|
||||||
|
- [Mypy typing documentation](https://mypy.readthedocs.io/en/stable/#overview-type-system-reference)
|
||||||
|
- [Python official typing documentation](https://docs.python.org/3/library/typing.html)
|
||||||
|
- [Typing in Python](https://inventwithpython.com/blog/2019/11/24/type-hints-for-busy-python-programmers/) article
|
||||||
|
|
||||||
|
Additionally, the following resources might be useful:
|
||||||
|
|
||||||
- [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)
|
- [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
|
- [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
|
- [Testing mypy stubs, plugins, and types](https://sobolevn.me/2019/08/testing-mypy-types) guide
|
||||||
|
- [Awesome Python Typing](https://github.com/typeddjango/awesome-python-typing) list
|
||||||
|
|
||||||
It is also recommended to take a look at these resources:
|
## Dev setup
|
||||||
|
|
||||||
- [Awesome Python Typing](https://github.com/typeddjango/awesome-python-typing)
|
### Repository Setup
|
||||||
|
|
||||||
|
As a first step you will need to fork this repository and clone your fork locally.
|
||||||
|
In order to be able to continously sync your fork with the origin repository's master branch, you will need to set up an upstream master. To do so follow this [official github guide](https://docs.github.com/en/free-pro-team@latest/github/collaborating-with-issues-and-pull-requests/syncing-a-fork).
|
||||||
|
|
||||||
## Dev documentation
|
### Dependency Setup
|
||||||
|
|
||||||
TODO
|
After your repository is setup you will then need to create and activate a git ignored virtual env, e.g.:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 -m venv .venv
|
||||||
|
source .venv/bin/activate
|
||||||
|
```
|
||||||
|
|
||||||
## Dependencies
|
Then install the dev requirements:
|
||||||
|
|
||||||
We use `pip` to manage the dependencies.
|
|
||||||
|
|
||||||
To install them you would need to activate your `virtualenv` and run `install` command:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pip install -r ./dev-requirements.txt
|
pip install -r ./dev-requirements.txt
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Finally, install the pre-commit hooks:
|
||||||
## 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
|
```bash
|
||||||
mypy ./mypy_django_plugin
|
pre-commit install
|
||||||
```
|
```
|
||||||
|
|
||||||
### Testing
|
### Testing and Linting
|
||||||
|
|
||||||
There are unit tests and type-related tests.
|
We use `mypy`, `pytest`, `flake8`, and `black` for quality control. All tools except pytest are executed using pre-commit when you make a commit.
|
||||||
|
To ensure there are not formatting or typing issues in the entire repository you can run:
|
||||||
|
|
||||||
To run unit tests:
|
```bash
|
||||||
|
pre-commit run --all-files
|
||||||
|
```
|
||||||
|
|
||||||
|
NOTE: This command will not only lint but also modify files - so make sure to commit whatever changes you've made before hand.
|
||||||
|
You can also run pre-commit per file or for a specific path, simply replace "--all-files" with a target (see [this guide](https://codeburst.io/tool-your-django-project-pre-commit-hooks-e1799d84551f) for more info).
|
||||||
|
|
||||||
|
To execute the unit tests, simply run:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pytest
|
pytest
|
||||||
```
|
```
|
||||||
|
|
||||||
Type-related tests ensure that different Django versions do work correctly.
|
We also test the stubs against the Django's own test suite. This is done in CI but you can also do this locally.
|
||||||
To run type-related tests:
|
To execute the script run:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python ./scripts/typecheck_tests.py --django_version=2.2
|
python ./scripts/typecheck_tests.py --django_version 3.0
|
||||||
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
|
### Generating Stubs using Stubgen
|
||||||
|
|
||||||
We use [trunk based](https://trunkbaseddevelopment.com/)
|
The stubs are based on auto-generated code created by Mypy's stubgen tool (see: [the stubgen docs](https://mypy.readthedocs.io/en/stable/stubgen.html)).
|
||||||
development (we also sometimes call it `wemake-git-flow`).
|
To make life easier we have a helper script that auto generates these stubs. To use it you can run:
|
||||||
|
|
||||||
What the point of this method?
|
```bash
|
||||||
|
python ./scripts/stubgen-django.py --django_version 3.1
|
||||||
|
```
|
||||||
|
|
||||||
1. We use protected `master` branch,
|
You can also pass an optional commit hash as a second kwarg to checkout a specific commit, e.g.
|
||||||
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
|
```bash
|
||||||
which frees us from merging hell and long-living branches.
|
python ./scripts/stubgen-django.py --django_version 3.1 --commit_sha <commit_sha>
|
||||||
|
```
|
||||||
|
|
||||||
In this method, the latest version of the app is always in the `master` branch.
|
The output for this is a gitignored folder called "stubgen" in the repo's root.
|
||||||
|
|
||||||
|
## Submission Guidelines
|
||||||
|
|
||||||
## Other help
|
The workflow for contributions is fairly simple:
|
||||||
|
|
||||||
You can contribute by spreading a word about this library.
|
1. fork and setup the repository as in the previous step.
|
||||||
It would also be a huge contribution to write
|
2. create a local branch.
|
||||||
a short article on how you are using this project.
|
3. make whatever changes you want to contribute.
|
||||||
You can also share your best practices with us.
|
4. ensure your contribution does not introduce linting issues or breaks the tests by linting and testing the code.
|
||||||
|
5. make a pull request with an adequate description.
|
||||||
|
|
||||||
|
## A Note About Generics
|
||||||
|
|
||||||
|
As Django uses a lot of the more dynamic features of Python (i.e. metaobjects), statically typing it requires heavy use of generics. Unfortunately, the syntax for generics is also valid python syntax. For instance, the statement `class SomeClass(SuperType[int])` implicitly translates to `class SomeClass(SuperType.__class_getitem__(int))`. If `SuperType` doesn't define the `__class_getitem__` method, this causes a runtime error, even if the code typechecks.
|
||||||
|
|
||||||
|
When adding a new generic class, or changing an existing class to use generics, run a quick test to see if it causes a runtime error. If it does, please add the new generic class to the `_need_generic` list in the [django_stubs_ext monkeypatch function](https://github.com/typeddjango/django-stubs/tree/master/django_stubs_ext/django_stubs_ext/monkeypatch.py)
|
||||||
|
|||||||
100
README.md
100
README.md
@@ -1,11 +1,12 @@
|
|||||||
<img src="http://mypy-lang.org/static/mypy_light.svg" alt="mypy logo" width="300px"/>
|
<img src="http://mypy-lang.org/static/mypy_light.svg" alt="mypy logo" width="300px"/>
|
||||||
|
|
||||||
# Typesafe Django Framework
|
# pep484 stubs for Django
|
||||||
|
|
||||||
[](https://travis-ci.com/typeddjango/django-stubs)
|
[](https://github.com/typeddjango/django-stubs/actions?query=workflow%3Atest)
|
||||||
[](http://mypy-lang.org/)
|
[](http://mypy-lang.org/)
|
||||||
[](https://gitter.im/mypy-django/Lobby)
|
[](https://gitter.im/mypy-django/Lobby)
|
||||||
|
|
||||||
|
|
||||||
This package contains [type stubs](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.
|
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.
|
||||||
|
|
||||||
|
|
||||||
@@ -15,12 +16,7 @@ This package contains [type stubs](https://www.python.org/dev/peps/pep-0561/) an
|
|||||||
pip install django-stubs
|
pip install django-stubs
|
||||||
```
|
```
|
||||||
|
|
||||||
See [Configuration](#configuration) section to get started.
|
To make mypy aware of the plugin, you need to add
|
||||||
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
To make `mypy` happy, you will need to add:
|
|
||||||
|
|
||||||
```ini
|
```ini
|
||||||
[mypy]
|
[mypy]
|
||||||
@@ -40,13 +36,13 @@ Two things happeining here:
|
|||||||
|
|
||||||
This fully working [typed boilerplate](https://github.com/wemake-services/wemake-django-template) can serve you as an example.
|
This fully working [typed boilerplate](https://github.com/wemake-services/wemake-django-template) can serve you as an example.
|
||||||
|
|
||||||
|
|
||||||
## Version compatibility
|
## Version compatibility
|
||||||
|
|
||||||
We rely on different `django` and `mypy` versions:
|
We rely on different `django` and `mypy` versions:
|
||||||
|
|
||||||
| django-stubs | mypy version | django version | python version
|
| django-stubs | mypy version | django version | python version
|
||||||
| ------------ | ---- | ---- | ---- |
|
| ------------ | ---- | ---- | ---- |
|
||||||
|
| 1.7.0 | 0.790 | 2.2.x \|\| 3.x | ^3.6
|
||||||
| 1.6.0 | 0.780 | 2.2.x \|\| 3.x | ^3.6
|
| 1.6.0 | 0.780 | 2.2.x \|\| 3.x | ^3.6
|
||||||
| 1.5.0 | 0.770 | 2.2.x \|\| 3.x | ^3.6
|
| 1.5.0 | 0.770 | 2.2.x \|\| 3.x | ^3.6
|
||||||
| 1.4.0 | 0.760 | 2.2.x \|\| 3.x | ^3.6
|
| 1.4.0 | 0.760 | 2.2.x \|\| 3.x | ^3.6
|
||||||
@@ -62,7 +58,7 @@ We rely on different `django` and `mypy` versions:
|
|||||||
|
|
||||||
No, it is not. We are independent from Django at the moment.
|
No, it is not. We are independent from Django at the moment.
|
||||||
There's a [proposal](https://github.com/django/deps/pull/65) to merge our project into the Django itself.
|
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.
|
You can show your support by liking the PR.
|
||||||
|
|
||||||
### Is it safe to use this in production?
|
### Is it safe to use this in production?
|
||||||
|
|
||||||
@@ -73,22 +69,36 @@ But, it does not make any sense to use this project without `mypy`.
|
|||||||
|
|
||||||
### mypy crashes when I run it with this plugin installed
|
### mypy crashes when I run it with this plugin installed
|
||||||
|
|
||||||
The current implementation uses Django runtime to extract models information, so it will crash, if your installed apps or `models.py` is not correct. For this same reason, you cannot use `reveal_type` inside global scope of any Python file that will be executed for `django.setup()`.
|
Current implementation uses Django runtime to extract models information, so it will crash, if your installed apps 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.
|
In other words, if your `manage.py runserver` crashes, mypy will crash too.
|
||||||
You can also run `mypy` with the [`--tb`](https://mypy.readthedocs.io/en/stable/command_line.html#cmdoption-mypy-show-traceback)
|
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 errors.
|
option to get extra information about the error.
|
||||||
|
|
||||||
### I cannot use QuerySet or Manager with type annotations
|
### I cannot use QuerySet or Manager with type annotations
|
||||||
|
|
||||||
You can get a `TypeError: 'type' object is not subscriptable`
|
You can get a `TypeError: 'type' object is not subscriptable`
|
||||||
when you will try to use `QuerySet[MyModel]` or `Manager[MyModel]`.
|
when you will try to use `QuerySet[MyModel]`, `Manager[MyModel]` or some other Django-based Generic types.
|
||||||
|
|
||||||
This happens because Django classes do not support [`__class_getitem__`](https://www.python.org/dev/peps/pep-0560/#class-getitem) magic method.
|
This happens because these Django classes do not support [`__class_getitem__`](https://www.python.org/dev/peps/pep-0560/#class-getitem) magic method in runtime.
|
||||||
|
|
||||||
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.
|
1. You can go with our [`django_stubs_ext`](https://github.com/typeddjango/django-stubs/tree/master/django_stubs_ext) helper, that patches all the types we use as Generic in django.
|
||||||
|
|
||||||
Currently we [are working](https://github.com/django/django/pull/12405) on providing `__class_getitem__` to the classes where we need them.
|
Install it:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install django-stubs-ext # as a production dependency
|
||||||
|
```
|
||||||
|
|
||||||
|
And then place in your top-level settings:
|
||||||
|
|
||||||
|
```python
|
||||||
|
import django_stubs_ext
|
||||||
|
|
||||||
|
django_stubs_ext.monkeypatch()
|
||||||
|
```
|
||||||
|
|
||||||
|
2. 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.
|
||||||
|
|
||||||
### How can I create a HttpRequest that's guaranteed to have an authenticated user?
|
### How can I create a HttpRequest that's guaranteed to have an authenticated user?
|
||||||
|
|
||||||
@@ -110,6 +120,51 @@ class AuthenticatedHttpRequest(HttpRequest):
|
|||||||
And then use `AuthenticatedHttpRequest` instead of the standard `HttpRequest` for when you know that the user is authenticated. For example in views using the `@login_required` decorator.
|
And then use `AuthenticatedHttpRequest` instead of the standard `HttpRequest` for when you know that the user is authenticated. For example in views using the `@login_required` decorator.
|
||||||
|
|
||||||
|
|
||||||
|
### My QuerySet methods are returning Any rather than my Model
|
||||||
|
|
||||||
|
`QuerySet.as_manager()` is not currently supported.
|
||||||
|
|
||||||
|
If you are using `MyQuerySet.as_manager()`, then your `Manager`/`QuerySet` methods will all not be linked to your model.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
class MyModelQuerySet(models.QuerySet):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class MyModel(models.Model):
|
||||||
|
bar = models.IntegerField()
|
||||||
|
objects = MyModelQuerySet.as_manager()
|
||||||
|
|
||||||
|
def use_my_model():
|
||||||
|
foo = MyModel.objects.get(id=1) # This is `Any` but it should be `MyModel`
|
||||||
|
return foo.xyz # No error, but there should be
|
||||||
|
```
|
||||||
|
|
||||||
|
There is a workaround: use `Manager.from_queryset` instead.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
class MyModelQuerySet(models.QuerySet):
|
||||||
|
pass
|
||||||
|
|
||||||
|
MyModelManager = models.Manager.from_queryset(MyModelQuerySet)
|
||||||
|
|
||||||
|
class MyModel(models.Model):
|
||||||
|
bar = models.IntegerField()
|
||||||
|
objects = MyModelManager()
|
||||||
|
|
||||||
|
def use_my_model():
|
||||||
|
foo = MyModel.objects.get(id=1)
|
||||||
|
return foo.xyz # Gives an error
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
## Related projects
|
## Related projects
|
||||||
|
|
||||||
- [`awesome-python-typing`](https://github.com/typeddjango/awesome-python-typing) - Awesome list of all typing-related things in Python.
|
- [`awesome-python-typing`](https://github.com/typeddjango/awesome-python-typing) - Awesome list of all typing-related things in Python.
|
||||||
@@ -122,5 +177,14 @@ And then use `AuthenticatedHttpRequest` instead of the standard `HttpRequest` fo
|
|||||||
## To get help
|
## To get help
|
||||||
|
|
||||||
We have Gitter here: <https://gitter.im/mypy-django/Lobby>
|
We have Gitter here: <https://gitter.im/mypy-django/Lobby>
|
||||||
|
If you think you have more generic typing issue, please refer to <https://github.com/python/mypy> and their Gitter.
|
||||||
|
|
||||||
If you think you have a more generic typing issue, please refer to <https://github.com/python/mypy> and their Gitter.
|
## Contributing
|
||||||
|
|
||||||
|
This project is open source and community driven. As such we encourage contributions big and small. You can contribute by doing any of the following:
|
||||||
|
|
||||||
|
1. Contribute code (e.g. improve stubs, add plugin capabilities, write tests etc) - to do so please follow the [contribution guide](./CONTRIBUTING.md).
|
||||||
|
2. Assist in code reviews and discussions in issues.
|
||||||
|
3. Identify bugs and issues and report these
|
||||||
|
|
||||||
|
You can always also reach out in gitter to discuss your contributions!
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
black
|
wheel
|
||||||
|
mypy==0.790
|
||||||
|
requests==2.24.0
|
||||||
|
coreapi==2.3.3
|
||||||
|
typing-extensions==3.7.4.3
|
||||||
|
gitpython==3.1.9
|
||||||
|
pre-commit==2.7.1
|
||||||
|
pytest==6.1.1
|
||||||
pytest-mypy-plugins==1.6.1
|
pytest-mypy-plugins==1.6.1
|
||||||
psycopg2-binary
|
psycopg2-binary
|
||||||
flake8==3.7.9
|
-e ./django_stubs_ext
|
||||||
flake8-pyi==19.3.0
|
|
||||||
isort==4.3.21
|
|
||||||
gitpython==3.1.0
|
|
||||||
-e .
|
-e .
|
||||||
|
|||||||
1
django-sources
Submodule
1
django-sources
Submodule
Submodule django-sources added at aa28213eb5
@@ -1,4 +1,4 @@
|
|||||||
from typing import Any, List, Union, Iterable, Optional
|
from typing import Any, List, Union, Optional, Sequence
|
||||||
|
|
||||||
from django.contrib.admin.options import BaseModelAdmin
|
from django.contrib.admin.options import BaseModelAdmin
|
||||||
from django.core.checks.messages import CheckMessage, Error
|
from django.core.checks.messages import CheckMessage, Error
|
||||||
@@ -7,7 +7,7 @@ from django.apps.config import AppConfig
|
|||||||
|
|
||||||
_CheckError = Union[str, Error]
|
_CheckError = Union[str, Error]
|
||||||
|
|
||||||
def check_admin_app(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[_CheckError]: ...
|
def check_admin_app(app_configs: Optional[Sequence[AppConfig]] = ..., **kwargs: Any) -> List[_CheckError]: ...
|
||||||
def check_dependencies(**kwargs: Any) -> List[_CheckError]: ...
|
def check_dependencies(**kwargs: Any) -> List[_CheckError]: ...
|
||||||
|
|
||||||
class BaseModelAdminChecks:
|
class BaseModelAdminChecks:
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
from typing import Any, Callable, Optional, Type
|
from typing import Any, Callable, Optional, Type
|
||||||
|
|
||||||
|
from django.contrib.admin.sites import AdminSite
|
||||||
from django.db.models.base import Model
|
from django.db.models.base import Model
|
||||||
|
|
||||||
def register(*models: Type[Model], site: Optional[Any] = ...) -> Callable: ...
|
def register(*models: Type[Model], site: Optional[AdminSite] = ...) -> Callable: ...
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ from typing import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
from django.forms.forms import BaseForm
|
from django.forms.forms import BaseForm
|
||||||
from django.forms.formsets import BaseFormSet
|
from django.forms.models import BaseInlineFormSet
|
||||||
from typing_extensions import Literal, TypedDict
|
from typing_extensions import Literal, TypedDict
|
||||||
|
|
||||||
from django.contrib.admin.filters import ListFilter
|
from django.contrib.admin.filters import ListFilter
|
||||||
@@ -154,7 +154,7 @@ class ModelAdmin(BaseModelAdmin[_ModelT]):
|
|||||||
delete_selected_confirmation_template: str = ...
|
delete_selected_confirmation_template: str = ...
|
||||||
object_history_template: str = ...
|
object_history_template: str = ...
|
||||||
popup_response_template: str = ...
|
popup_response_template: str = ...
|
||||||
actions: Sequence[Union[Callable[[ModelAdmin, HttpRequest, QuerySet], None], str]] = ...
|
actions: Optional[Sequence[Union[Callable[[ModelAdmin, HttpRequest, QuerySet], None], str]]] = ...
|
||||||
action_form: Any = ...
|
action_form: Any = ...
|
||||||
actions_on_top: bool = ...
|
actions_on_top: bool = ...
|
||||||
actions_on_bottom: bool = ...
|
actions_on_bottom: bool = ...
|
||||||
@@ -163,6 +163,7 @@ class ModelAdmin(BaseModelAdmin[_ModelT]):
|
|||||||
opts: Options = ...
|
opts: Options = ...
|
||||||
admin_site: AdminSite = ...
|
admin_site: AdminSite = ...
|
||||||
def __init__(self, model: Type[_ModelT], admin_site: Optional[AdminSite]) -> None: ...
|
def __init__(self, model: Type[_ModelT], admin_site: Optional[AdminSite]) -> None: ...
|
||||||
|
def get_inlines(self, request: HttpRequest, obj: Optional[_ModelT] = ...) -> List[Type[InlineModelAdmin]]: ...
|
||||||
def get_inline_instances(self, request: HttpRequest, obj: Optional[_ModelT] = ...) -> List[InlineModelAdmin]: ...
|
def get_inline_instances(self, request: HttpRequest, obj: Optional[_ModelT] = ...) -> List[InlineModelAdmin]: ...
|
||||||
def get_urls(self) -> List[URLPattern]: ...
|
def get_urls(self) -> List[URLPattern]: ...
|
||||||
@property
|
@property
|
||||||
@@ -268,7 +269,7 @@ class ModelAdmin(BaseModelAdmin[_ModelT]):
|
|||||||
class InlineModelAdmin(BaseModelAdmin[_ModelT]):
|
class InlineModelAdmin(BaseModelAdmin[_ModelT]):
|
||||||
model: Type[_ModelT] = ...
|
model: Type[_ModelT] = ...
|
||||||
fk_name: str = ...
|
fk_name: str = ...
|
||||||
formset: BaseFormSet = ...
|
formset: Type[BaseInlineFormSet] = ...
|
||||||
extra: int = ...
|
extra: int = ...
|
||||||
min_num: Optional[int] = ...
|
min_num: Optional[int] = ...
|
||||||
max_num: Optional[int] = ...
|
max_num: Optional[int] = ...
|
||||||
|
|||||||
@@ -1,36 +1,51 @@
|
|||||||
|
import sys
|
||||||
from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple, Type, Union
|
from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple, Type, Union
|
||||||
|
|
||||||
|
from django.apps.config import AppConfig
|
||||||
from django.contrib.admin.options import ModelAdmin
|
from django.contrib.admin.options import ModelAdmin
|
||||||
|
from django.contrib.auth.forms import AuthenticationForm
|
||||||
from django.core.handlers.wsgi import WSGIRequest
|
from django.core.handlers.wsgi import WSGIRequest
|
||||||
from django.db.models.base import Model
|
from django.db.models.base import Model
|
||||||
|
from django.db.models.query import QuerySet
|
||||||
from django.http.response import HttpResponse
|
from django.http.response import HttpResponse
|
||||||
from django.template.response import TemplateResponse
|
from django.template.response import TemplateResponse
|
||||||
from django.urls.resolvers import URLResolver
|
from django.urls.resolvers import URLResolver
|
||||||
from django.utils.functional import LazyObject
|
from django.utils.functional import LazyObject
|
||||||
|
from django.core.checks import CheckMessage
|
||||||
|
|
||||||
from django.apps.config import AppConfig
|
if sys.version_info >= (3, 9):
|
||||||
|
from weakref import WeakSet
|
||||||
|
|
||||||
all_sites: Any
|
all_sites: WeakSet[AdminSite]
|
||||||
|
else:
|
||||||
|
from typing import MutableSet
|
||||||
|
|
||||||
|
all_sites: MutableSet[AdminSite]
|
||||||
|
|
||||||
|
_ActionCallback = Callable[[ModelAdmin, WSGIRequest, QuerySet], Optional[TemplateResponse]]
|
||||||
|
|
||||||
class AlreadyRegistered(Exception): ...
|
class AlreadyRegistered(Exception): ...
|
||||||
class NotRegistered(Exception): ...
|
class NotRegistered(Exception): ...
|
||||||
|
|
||||||
class AdminSite:
|
class AdminSite:
|
||||||
site_title: Any = ...
|
site_title: str = ...
|
||||||
site_header: Any = ...
|
site_header: str = ...
|
||||||
index_title: Any = ...
|
index_title: str = ...
|
||||||
site_url: str = ...
|
site_url: str = ...
|
||||||
login_form: Any = ...
|
login_form: Optional[AuthenticationForm] = ...
|
||||||
index_template: Any = ...
|
index_template: Optional[str] = ...
|
||||||
app_index_template: Any = ...
|
app_index_template: Optional[str] = ...
|
||||||
login_template: Any = ...
|
login_template: Optional[str] = ...
|
||||||
logout_template: Any = ...
|
logout_template: Optional[str] = ...
|
||||||
password_change_template: Any = ...
|
password_change_template: Optional[str] = ...
|
||||||
password_change_done_template: Any = ...
|
password_change_done_template: Optional[str] = ...
|
||||||
name: str = ...
|
name: str = ...
|
||||||
|
_empty_value_display: str = ...
|
||||||
_registry: Dict[Type[Model], ModelAdmin]
|
_registry: Dict[Type[Model], ModelAdmin]
|
||||||
|
_global_actions: Dict[str, _ActionCallback]
|
||||||
|
_actions: Dict[str, _ActionCallback]
|
||||||
def __init__(self, name: str = ...) -> None: ...
|
def __init__(self, name: str = ...) -> None: ...
|
||||||
def check(self, app_configs: Optional[Iterable[AppConfig]]) -> List[Any]: ...
|
def check(self, app_configs: Optional[Iterable[AppConfig]]) -> List[CheckMessage]: ...
|
||||||
def register(
|
def register(
|
||||||
self,
|
self,
|
||||||
model_or_iterable: Union[Type[Model], Iterable[Type[Model]]],
|
model_or_iterable: Union[Type[Model], Iterable[Type[Model]]],
|
||||||
@@ -39,28 +54,28 @@ class AdminSite:
|
|||||||
) -> None: ...
|
) -> None: ...
|
||||||
def unregister(self, model_or_iterable: Union[Type[Model], Iterable[Type[Model]]]) -> None: ...
|
def unregister(self, model_or_iterable: Union[Type[Model], Iterable[Type[Model]]]) -> None: ...
|
||||||
def is_registered(self, model: Type[Model]) -> bool: ...
|
def is_registered(self, model: Type[Model]) -> bool: ...
|
||||||
def add_action(self, action: Callable, name: Optional[str] = ...) -> None: ...
|
def add_action(self, action: _ActionCallback, name: Optional[str] = ...) -> None: ...
|
||||||
def disable_action(self, name: str) -> None: ...
|
def disable_action(self, name: str) -> None: ...
|
||||||
def get_action(self, name: str) -> Callable: ...
|
def get_action(self, name: str) -> Callable: ...
|
||||||
@property
|
@property
|
||||||
def actions(self): ...
|
def actions(self) -> Iterable[Tuple[str, _ActionCallback]]: ...
|
||||||
@property
|
@property
|
||||||
def empty_value_display(self): ...
|
def empty_value_display(self) -> str: ...
|
||||||
@empty_value_display.setter
|
@empty_value_display.setter
|
||||||
def empty_value_display(self, empty_value_display: Any) -> None: ...
|
def empty_value_display(self, empty_value_display: str) -> None: ...
|
||||||
def has_permission(self, request: WSGIRequest) -> bool: ...
|
def has_permission(self, request: WSGIRequest) -> bool: ...
|
||||||
def admin_view(self, view: Callable, cacheable: bool = ...) -> Callable: ...
|
def admin_view(self, view: Callable, cacheable: bool = ...) -> Callable: ...
|
||||||
def get_urls(self) -> List[URLResolver]: ...
|
def get_urls(self) -> List[URLResolver]: ...
|
||||||
@property
|
@property
|
||||||
def urls(self) -> Tuple[List[URLResolver], str, str]: ...
|
def urls(self) -> Tuple[List[URLResolver], str, str]: ...
|
||||||
def each_context(self, request: Any): ...
|
def each_context(self, request: WSGIRequest) -> Dict[str, Any]: ...
|
||||||
def password_change(
|
def password_change(
|
||||||
self, request: WSGIRequest, extra_context: Optional[Dict[str, Any]] = ...
|
self, request: WSGIRequest, extra_context: Optional[Dict[str, Any]] = ...
|
||||||
) -> TemplateResponse: ...
|
) -> TemplateResponse: ...
|
||||||
def password_change_done(
|
def password_change_done(
|
||||||
self, request: WSGIRequest, extra_context: Optional[Dict[str, Any]] = ...
|
self, request: WSGIRequest, extra_context: Optional[Dict[str, Any]] = ...
|
||||||
) -> TemplateResponse: ...
|
) -> TemplateResponse: ...
|
||||||
def i18n_javascript(self, request: WSGIRequest, extra_context: Optional[Dict[Any, Any]] = ...) -> HttpResponse: ...
|
def i18n_javascript(self, request: WSGIRequest, extra_context: Optional[Dict[str, Any]] = ...) -> HttpResponse: ...
|
||||||
def logout(self, request: WSGIRequest, extra_context: Optional[Dict[str, Any]] = ...) -> TemplateResponse: ...
|
def logout(self, request: WSGIRequest, extra_context: Optional[Dict[str, Any]] = ...) -> TemplateResponse: ...
|
||||||
def login(self, request: WSGIRequest, extra_context: Optional[Dict[str, Any]] = ...) -> HttpResponse: ...
|
def login(self, request: WSGIRequest, extra_context: Optional[Dict[str, Any]] = ...) -> HttpResponse: ...
|
||||||
def _build_app_dict(self, request: WSGIRequest, label: Optional[str] = ...) -> Dict[str, Any]: ...
|
def _build_app_dict(self, request: WSGIRequest, label: Optional[str] = ...) -> Dict[str, Any]: ...
|
||||||
@@ -72,4 +87,4 @@ class AdminSite:
|
|||||||
|
|
||||||
class DefaultAdminSite(LazyObject): ...
|
class DefaultAdminSite(LazyObject): ...
|
||||||
|
|
||||||
site: Any
|
site: AdminSite
|
||||||
|
|||||||
@@ -7,12 +7,11 @@ from django.core.handlers.wsgi import WSGIRequest
|
|||||||
from django.db.models.base import Model
|
from django.db.models.base import Model
|
||||||
from django.db.models.options import Options
|
from django.db.models.options import Options
|
||||||
from django.http.request import HttpRequest
|
from django.http.request import HttpRequest
|
||||||
|
from django.test.client import Client
|
||||||
|
|
||||||
from .signals import (
|
from .signals import user_logged_in as user_logged_in
|
||||||
user_logged_in as user_logged_in,
|
from .signals import user_logged_out as user_logged_out
|
||||||
user_logged_out as user_logged_out,
|
from .signals import user_login_failed as user_login_failed
|
||||||
user_login_failed as user_login_failed,
|
|
||||||
)
|
|
||||||
|
|
||||||
SESSION_KEY: str
|
SESSION_KEY: str
|
||||||
BACKEND_SESSION_KEY: str
|
BACKEND_SESSION_KEY: str
|
||||||
@@ -27,7 +26,7 @@ def login(
|
|||||||
) -> None: ...
|
) -> None: ...
|
||||||
def logout(request: HttpRequest) -> None: ...
|
def logout(request: HttpRequest) -> None: ...
|
||||||
def get_user_model() -> Type[Model]: ...
|
def get_user_model() -> Type[Model]: ...
|
||||||
def get_user(request: HttpRequest) -> Union[AbstractBaseUser, AnonymousUser]: ...
|
def get_user(request: Union[HttpRequest, Client]) -> Union[AbstractBaseUser, AnonymousUser]: ...
|
||||||
def get_permission_codename(action: str, opts: Options) -> str: ...
|
def get_permission_codename(action: str, opts: Options) -> str: ...
|
||||||
def update_session_auth_hash(request: HttpRequest, user: AbstractBaseUser) -> None: ...
|
def update_session_auth_hash(request: HttpRequest, user: AbstractBaseUser) -> None: ...
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ from django.contrib.auth.base_user import AbstractBaseUser
|
|||||||
from django.contrib.auth.models import AnonymousUser, User, Permission
|
from django.contrib.auth.models import AnonymousUser, User, Permission
|
||||||
|
|
||||||
from django.db.models.base import Model
|
from django.db.models.base import Model
|
||||||
|
from django.http.request import HttpRequest
|
||||||
|
|
||||||
_AnyUser = Union[Model, AnonymousUser]
|
_AnyUser = Union[Model, AnonymousUser]
|
||||||
|
|
||||||
@@ -11,7 +12,7 @@ UserModel: Any
|
|||||||
|
|
||||||
class BaseBackend:
|
class BaseBackend:
|
||||||
def authenticate(
|
def authenticate(
|
||||||
self, request: Any, username: Optional[str] = ..., password: Optional[str] = ..., **kwargs: Any
|
self, request: HttpRequest, username: Optional[str] = ..., password: Optional[str] = ..., **kwargs: Any
|
||||||
) -> Optional[AbstractBaseUser]: ...
|
) -> Optional[AbstractBaseUser]: ...
|
||||||
def get_user(self, user_id: int) -> Optional[AbstractBaseUser]: ...
|
def get_user(self, user_id: int) -> Optional[AbstractBaseUser]: ...
|
||||||
def get_user_permissions(self, user_obj: _AnyUser, obj: Optional[Model] = ...) -> Set[str]: ...
|
def get_user_permissions(self, user_obj: _AnyUser, obj: Optional[Model] = ...) -> Set[str]: ...
|
||||||
@@ -35,6 +36,6 @@ class AllowAllUsersModelBackend(ModelBackend): ...
|
|||||||
class RemoteUserBackend(ModelBackend):
|
class RemoteUserBackend(ModelBackend):
|
||||||
create_unknown_user: bool = ...
|
create_unknown_user: bool = ...
|
||||||
def clean_username(self, username: str) -> str: ...
|
def clean_username(self, username: str) -> str: ...
|
||||||
def configure_user(self, user: User) -> User: ...
|
def configure_user(self, request: HttpRequest, user: User) -> User: ...
|
||||||
|
|
||||||
class AllowAllUsersRemoteUserBackend(RemoteUserBackend): ...
|
class AllowAllUsersRemoteUserBackend(RemoteUserBackend): ...
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import sys
|
import sys
|
||||||
from typing import Any, Optional, Tuple, List, overload, TypeVar
|
from typing import Any, Optional, Tuple, List, overload, TypeVar, Union
|
||||||
|
|
||||||
from django.db.models.base import Model
|
from django.db.models.base import Model
|
||||||
|
from django.db.models.expressions import Combinable
|
||||||
|
from django.db.models.fields import BooleanField
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
if sys.version_info < (3, 8):
|
if sys.version_info < (3, 8):
|
||||||
@@ -23,6 +24,7 @@ class AbstractBaseUser(models.Model):
|
|||||||
|
|
||||||
password = models.CharField(max_length=128)
|
password = models.CharField(max_length=128)
|
||||||
last_login = models.DateTimeField(blank=True, null=True)
|
last_login = models.DateTimeField(blank=True, null=True)
|
||||||
|
is_active: Union[bool, BooleanField[Union[bool, Combinable], bool]] = ...
|
||||||
def get_username(self) -> str: ...
|
def get_username(self) -> str: ...
|
||||||
def natural_key(self) -> Tuple[str]: ...
|
def natural_key(self) -> Tuple[str]: ...
|
||||||
@property
|
@property
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
from typing import Any, List, Iterable, Optional
|
from typing import Any, List, Optional, Sequence
|
||||||
|
|
||||||
from django.core.checks.messages import CheckMessage
|
from django.core.checks.messages import CheckMessage
|
||||||
|
|
||||||
from django.apps.config import AppConfig
|
from django.apps.config import AppConfig
|
||||||
|
|
||||||
def check_user_model(app_configs: Optional[Iterable[AppConfig]] = ..., **kwargs: Any) -> List[CheckMessage]: ...
|
def check_user_model(app_configs: Optional[Sequence[AppConfig]] = ..., **kwargs: Any) -> List[CheckMessage]: ...
|
||||||
def check_models_permissions(app_configs: Optional[Iterable[AppConfig]] = ..., **kwargs: Any) -> List[Any]: ...
|
def check_models_permissions(app_configs: Optional[Sequence[AppConfig]] = ..., **kwargs: Any) -> List[Any]: ...
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
from datetime import date
|
||||||
from typing import Any, Optional
|
from typing import Any, Optional
|
||||||
|
|
||||||
from django.contrib.auth.base_user import AbstractBaseUser
|
from django.contrib.auth.base_user import AbstractBaseUser
|
||||||
@@ -5,7 +6,12 @@ from django.contrib.auth.base_user import AbstractBaseUser
|
|||||||
class PasswordResetTokenGenerator:
|
class PasswordResetTokenGenerator:
|
||||||
key_salt: str = ...
|
key_salt: str = ...
|
||||||
secret: Any = ...
|
secret: Any = ...
|
||||||
|
algorithm: str = ...
|
||||||
def make_token(self, user: AbstractBaseUser) -> str: ...
|
def make_token(self, user: AbstractBaseUser) -> str: ...
|
||||||
def check_token(self, user: Optional[AbstractBaseUser], token: Optional[str]) -> bool: ...
|
def check_token(self, user: Optional[AbstractBaseUser], token: Optional[str]) -> bool: ...
|
||||||
|
def _make_token_with_timestamp(self, user: AbstractBaseUser, timestamp: int) -> str: ...
|
||||||
|
def _make_hash_value(self, user: AbstractBaseUser, timestamp: int) -> str: ...
|
||||||
|
def _num_days(self, dt: date) -> float: ...
|
||||||
|
def _today(self) -> date: ...
|
||||||
|
|
||||||
default_token_generator: Any
|
default_token_generator: Any
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
from typing import Any, Optional, Set
|
from typing import Any, Optional, Set
|
||||||
|
|
||||||
from django.contrib.auth.base_user import AbstractBaseUser
|
from django.contrib.auth.base_user import AbstractBaseUser
|
||||||
|
from django.contrib.auth.forms import AuthenticationForm
|
||||||
from django.core.handlers.wsgi import WSGIRequest
|
from django.core.handlers.wsgi import WSGIRequest
|
||||||
from django.http.request import HttpRequest
|
from django.http.request import HttpRequest
|
||||||
from django.http.response import HttpResponseRedirect
|
from django.http.response import HttpResponseRedirect
|
||||||
@@ -14,7 +15,7 @@ class SuccessURLAllowedHostsMixin:
|
|||||||
success_url_allowed_hosts: Any = ...
|
success_url_allowed_hosts: Any = ...
|
||||||
def get_success_url_allowed_hosts(self) -> Set[str]: ...
|
def get_success_url_allowed_hosts(self) -> Set[str]: ...
|
||||||
|
|
||||||
class LoginView(SuccessURLAllowedHostsMixin, FormView):
|
class LoginView(SuccessURLAllowedHostsMixin, FormView[AuthenticationForm]):
|
||||||
authentication_form: Any = ...
|
authentication_form: Any = ...
|
||||||
redirect_field_name: Any = ...
|
redirect_field_name: Any = ...
|
||||||
redirect_authenticated_user: bool = ...
|
redirect_authenticated_user: bool = ...
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
from typing import Any, List, Iterable, Optional
|
from typing import Any, List, Optional, Sequence
|
||||||
|
|
||||||
from django.apps.config import AppConfig
|
from django.apps.config import AppConfig
|
||||||
|
|
||||||
def check_generic_foreign_keys(app_configs: Optional[Iterable[AppConfig]] = ..., **kwargs: Any) -> List[Any]: ...
|
def check_generic_foreign_keys(app_configs: Optional[Sequence[AppConfig]] = ..., **kwargs: Any) -> List[Any]: ...
|
||||||
def check_model_name_lengths(app_configs: Optional[Iterable[AppConfig]] = ..., **kwargs: Any) -> List[Any]: ...
|
def check_model_name_lengths(app_configs: Optional[Sequence[AppConfig]] = ..., **kwargs: Any) -> List[Any]: ...
|
||||||
|
|||||||
@@ -83,7 +83,6 @@ class GenericRelation(ForeignObject):
|
|||||||
def resolve_related_fields(self) -> List[Tuple[PositiveIntegerField, Field]]: ...
|
def resolve_related_fields(self) -> List[Tuple[PositiveIntegerField, Field]]: ...
|
||||||
def get_path_info(self, filtered_relation: Optional[FilteredRelation] = ...) -> List[PathInfo]: ...
|
def get_path_info(self, filtered_relation: Optional[FilteredRelation] = ...) -> List[PathInfo]: ...
|
||||||
def get_reverse_path_info(self, filtered_relation: None = ...) -> List[PathInfo]: ...
|
def get_reverse_path_info(self, filtered_relation: None = ...) -> List[PathInfo]: ...
|
||||||
def value_to_string(self, obj: Model) -> str: ...
|
|
||||||
def get_content_type(self) -> ContentType: ...
|
def get_content_type(self) -> ContentType: ...
|
||||||
def get_extra_restriction(
|
def get_extra_restriction(
|
||||||
self, where_class: Type[WhereNode], alias: Optional[str], remote_alias: str
|
self, where_class: Type[WhereNode], alias: Optional[str], remote_alias: str
|
||||||
|
|||||||
@@ -8,3 +8,5 @@ from .factory import fromfile as fromfile, fromstr as fromstr
|
|||||||
from .geometry import GEOSGeometry as GEOSGeometry, hex_regex as hex_regex, wkt_regex as wkt_regex
|
from .geometry import GEOSGeometry as GEOSGeometry, hex_regex as hex_regex, wkt_regex as wkt_regex
|
||||||
from .io import WKBReader as WKBReader, WKBWriter as WKBWriter, WKTReader as WKTReader, WKTWriter as WKTWriter
|
from .io import WKBReader as WKBReader, WKBWriter as WKBWriter, WKTReader as WKTReader, WKTWriter as WKTWriter
|
||||||
from .linestring import LineString as LineString, LinearRing as LinearRing
|
from .linestring import LineString as LineString, LinearRing as LinearRing
|
||||||
|
from .point import Point as Point
|
||||||
|
from .polygon import Polygon as Polygon
|
||||||
|
|||||||
@@ -1,14 +1,22 @@
|
|||||||
from datetime import date, datetime as datetime
|
from datetime import date, datetime as datetime
|
||||||
from typing import Any, Optional, SupportsInt, Union
|
from typing import Any, Callable, Dict, Optional, SupportsInt, Tuple, Type, Union
|
||||||
|
from django import template
|
||||||
|
|
||||||
register: Any
|
register: template.Library
|
||||||
|
|
||||||
def ordinal(value: Optional[Union[str, SupportsInt]]) -> Optional[str]: ...
|
def ordinal(value: Optional[Union[str, SupportsInt]]) -> Optional[str]: ...
|
||||||
def intcomma(value: Optional[Union[str, SupportsInt]], use_l10n: bool = ...) -> str: ...
|
def intcomma(value: Optional[Union[str, SupportsInt]], use_l10n: bool = ...) -> str: ...
|
||||||
|
|
||||||
intword_converters: Any
|
intword_converters: Tuple[Tuple[int, Callable]]
|
||||||
|
|
||||||
def intword(value: Optional[Union[str, SupportsInt]]) -> 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 apnumber(value: Optional[Union[str, SupportsInt]]) -> Optional[Union[int, str]]: ...
|
||||||
def naturalday(value: Optional[Union[date, str]], arg: None = ...) -> Optional[str]: ...
|
def naturalday(value: Optional[Union[date, str]], arg: None = ...) -> Optional[str]: ...
|
||||||
def naturaltime(value: datetime) -> str: ...
|
def naturaltime(value: datetime) -> str: ...
|
||||||
|
|
||||||
|
class NaturalTimeFormatter:
|
||||||
|
time_strings: Dict[str, str]
|
||||||
|
past_substrings: Dict[str, str]
|
||||||
|
future_substrings: Dict[str, str]
|
||||||
|
@classmethod
|
||||||
|
def string_for(cls: Type[NaturalTimeFormatter], value: Any) -> Any: ...
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ class JSONField(CheckFieldDefaultMixin, Field):
|
|||||||
encoder: Optional[Type[JSONEncoder]] = ...,
|
encoder: Optional[Type[JSONEncoder]] = ...,
|
||||||
**kwargs: Any
|
**kwargs: Any
|
||||||
) -> None: ...
|
) -> None: ...
|
||||||
def value_to_string(self, obj: Any): ...
|
|
||||||
|
|
||||||
class KeyTransform(Transform):
|
class KeyTransform(Transform):
|
||||||
operator: str = ...
|
operator: str = ...
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ class RangeField(models.Field):
|
|||||||
range_type: Any = ...
|
range_type: Any = ...
|
||||||
def get_prep_value(self, value: Any): ...
|
def get_prep_value(self, value: Any): ...
|
||||||
def to_python(self, value: Any): ...
|
def to_python(self, value: Any): ...
|
||||||
def value_to_string(self, obj: Any): ...
|
|
||||||
|
|
||||||
class IntegerRangeField(RangeField):
|
class IntegerRangeField(RangeField):
|
||||||
def __get__(self, instance, owner) -> NumericRange: ...
|
def __get__(self, instance, owner) -> NumericRange: ...
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
from typing import Any, List, Iterable, Optional
|
from typing import Any, List, Optional, Sequence
|
||||||
|
|
||||||
from django.core.checks.messages import Error
|
from django.core.checks.messages import Error
|
||||||
|
|
||||||
from django.apps.config import AppConfig
|
from django.apps.config import AppConfig
|
||||||
|
|
||||||
def check_finders(app_configs: Optional[Iterable[AppConfig]] = ..., **kwargs: Any) -> List[Error]: ...
|
def check_finders(app_configs: Optional[Sequence[AppConfig]] = ..., **kwargs: Any) -> List[Error]: ...
|
||||||
|
|||||||
@@ -1 +1,3 @@
|
|||||||
def get_asgi_application(): ...
|
from django.core.handlers.asgi import ASGIHandler
|
||||||
|
|
||||||
|
def get_asgi_application() -> ASGIHandler: ...
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
from typing import Any
|
from typing import Any, Optional, Sequence
|
||||||
|
|
||||||
|
from django.apps.config import AppConfig
|
||||||
|
|
||||||
E001: Any
|
E001: Any
|
||||||
|
|
||||||
def check_async_unsafe(app_configs: Any, **kwargs: Any): ...
|
def check_async_unsafe(app_configs: Optional[Sequence[AppConfig]] = ..., **kwargs: Any): ...
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from typing import Any, List, Iterable, Optional
|
from typing import Any, List, Optional, Sequence
|
||||||
|
|
||||||
from django.core.checks.messages import Error
|
from django.core.checks.messages import Error
|
||||||
|
|
||||||
@@ -6,4 +6,6 @@ from django.apps.config import AppConfig
|
|||||||
|
|
||||||
E001: Any
|
E001: Any
|
||||||
|
|
||||||
def check_default_cache_is_configured(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Error]: ...
|
def check_default_cache_is_configured(
|
||||||
|
app_configs: Optional[Sequence[AppConfig]] = ..., **kwargs: Any
|
||||||
|
) -> List[Error]: ...
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
from typing import Any, List, Iterable, Optional
|
from typing import Any, List, Optional, Sequence
|
||||||
|
|
||||||
from django.core.checks.messages import Warning
|
from django.core.checks.messages import Warning
|
||||||
|
|
||||||
from django.apps.config import AppConfig
|
from django.apps.config import AppConfig
|
||||||
|
|
||||||
def check_all_models(app_configs: Optional[Iterable[AppConfig]] = ..., **kwargs: Any) -> List[Warning]: ...
|
def check_all_models(app_configs: Optional[Sequence[AppConfig]] = ..., **kwargs: Any) -> List[Warning]: ...
|
||||||
def check_lazy_references(app_configs: Optional[Iterable[AppConfig]] = ..., **kwargs: Any) -> List[Any]: ...
|
def check_lazy_references(app_configs: Optional[Sequence[AppConfig]] = ..., **kwargs: Any) -> List[Any]: ...
|
||||||
|
|||||||
@@ -15,17 +15,19 @@ class Tags:
|
|||||||
translation: str = ...
|
translation: str = ...
|
||||||
urls: str = ...
|
urls: str = ...
|
||||||
|
|
||||||
|
_CheckCallable = Callable[..., List[CheckMessage]]
|
||||||
|
|
||||||
class CheckRegistry:
|
class CheckRegistry:
|
||||||
registered_checks: Set[Callable] = ...
|
registered_checks: Set[Callable] = ...
|
||||||
deployment_checks: Set[Callable] = ...
|
deployment_checks: Set[Callable] = ...
|
||||||
def __init__(self) -> None: ...
|
def __init__(self) -> None: ...
|
||||||
def register(self, check: Optional[Union[Callable, str]] = ..., *tags: Any, **kwargs: Any) -> Callable: ...
|
def register(self, check: Optional[Union[_CheckCallable, str]] = ..., *tags: str, **kwargs: Any) -> Callable: ...
|
||||||
def run_checks(
|
def run_checks(
|
||||||
self,
|
self,
|
||||||
app_configs: Optional[List[AppConfig]] = ...,
|
app_configs: Optional[List[AppConfig]] = ...,
|
||||||
tags: Optional[List[str]] = ...,
|
tags: Optional[List[str]] = ...,
|
||||||
include_deployment_checks: bool = ...,
|
include_deployment_checks: bool = ...,
|
||||||
) -> Union[List[CheckMessage], List[int], List[str]]: ...
|
) -> List[CheckMessage]: ...
|
||||||
def tag_exists(self, tag: str, include_deployment_checks: bool = ...) -> bool: ...
|
def tag_exists(self, tag: str, include_deployment_checks: bool = ...) -> bool: ...
|
||||||
def tags_available(self, deployment_checks: bool = ...) -> Set[str]: ...
|
def tags_available(self, deployment_checks: bool = ...) -> Set[str]: ...
|
||||||
def get_checks(self, include_deployment_checks: bool = ...) -> List[Callable]: ...
|
def get_checks(self, include_deployment_checks: bool = ...) -> List[Callable]: ...
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from typing import Any, List, Iterable, Optional
|
from typing import Any, List, Optional, Sequence
|
||||||
|
|
||||||
from django.core.checks.messages import Warning
|
from django.core.checks.messages import Warning
|
||||||
|
|
||||||
@@ -19,15 +19,17 @@ W019: Any
|
|||||||
W020: Any
|
W020: Any
|
||||||
W021: Any
|
W021: Any
|
||||||
|
|
||||||
def check_security_middleware(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Warning]: ...
|
def check_security_middleware(app_configs: Optional[Sequence[AppConfig]] = ..., **kwargs: Any) -> List[Warning]: ...
|
||||||
def check_xframe_options_middleware(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Warning]: ...
|
def check_xframe_options_middleware(
|
||||||
def check_sts(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Warning]: ...
|
app_configs: Optional[Sequence[AppConfig]] = ..., **kwargs: Any
|
||||||
def check_sts_include_subdomains(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Warning]: ...
|
) -> List[Warning]: ...
|
||||||
def check_sts_preload(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Warning]: ...
|
def check_sts(app_configs: Optional[Sequence[AppConfig]] = ..., **kwargs: Any) -> List[Warning]: ...
|
||||||
def check_content_type_nosniff(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Warning]: ...
|
def check_sts_include_subdomains(app_configs: Optional[Sequence[AppConfig]] = ..., **kwargs: Any) -> List[Warning]: ...
|
||||||
def check_xss_filter(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Warning]: ...
|
def check_sts_preload(app_configs: Optional[Sequence[AppConfig]] = ..., **kwargs: Any) -> List[Warning]: ...
|
||||||
def check_ssl_redirect(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Warning]: ...
|
def check_content_type_nosniff(app_configs: Optional[Sequence[AppConfig]] = ..., **kwargs: Any) -> List[Warning]: ...
|
||||||
def check_secret_key(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Warning]: ...
|
def check_xss_filter(app_configs: Optional[Sequence[AppConfig]] = ..., **kwargs: Any) -> List[Warning]: ...
|
||||||
def check_debug(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Warning]: ...
|
def check_ssl_redirect(app_configs: Optional[Sequence[AppConfig]] = ..., **kwargs: Any) -> List[Warning]: ...
|
||||||
def check_xframe_deny(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Warning]: ...
|
def check_secret_key(app_configs: Optional[Sequence[AppConfig]] = ..., **kwargs: Any) -> List[Warning]: ...
|
||||||
def check_allowed_hosts(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Warning]: ...
|
def check_debug(app_configs: Optional[Sequence[AppConfig]] = ..., **kwargs: Any) -> List[Warning]: ...
|
||||||
|
def check_xframe_deny(app_configs: Optional[Sequence[AppConfig]] = ..., **kwargs: Any) -> List[Warning]: ...
|
||||||
|
def check_allowed_hosts(app_configs: Optional[Sequence[AppConfig]] = ..., **kwargs: Any) -> List[Warning]: ...
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
from typing import Any, List, Iterable, Optional
|
from typing import Any, List, Optional, Sequence
|
||||||
|
|
||||||
from django.core.checks.messages import Warning
|
|
||||||
|
|
||||||
from django.apps.config import AppConfig
|
from django.apps.config import AppConfig
|
||||||
|
from django.core.checks.messages import Warning
|
||||||
|
|
||||||
W003: Any
|
W003: Any
|
||||||
W016: Any
|
W016: Any
|
||||||
|
|
||||||
def check_csrf_middleware(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Warning]: ...
|
def check_csrf_middleware(app_configs: Optional[Sequence[AppConfig]] = ..., **kwargs: Any) -> List[Warning]: ...
|
||||||
def check_csrf_cookie_secure(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Warning]: ...
|
def check_csrf_cookie_secure(app_configs: Optional[Sequence[AppConfig]] = ..., **kwargs: Any) -> List[Warning]: ...
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from typing import Any, List, Iterable, Optional
|
from typing import Any, List, Optional, Sequence
|
||||||
|
|
||||||
from django.core.checks.messages import Warning
|
from django.core.checks.messages import Warning
|
||||||
|
|
||||||
@@ -16,5 +16,5 @@ W013: Any
|
|||||||
W014: Any
|
W014: Any
|
||||||
W015: Any
|
W015: Any
|
||||||
|
|
||||||
def check_session_cookie_secure(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Warning]: ...
|
def check_session_cookie_secure(app_configs: Optional[Sequence[AppConfig]] = ..., **kwargs: Any) -> List[Warning]: ...
|
||||||
def check_session_cookie_httponly(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Warning]: ...
|
def check_session_cookie_httponly(app_configs: Optional[Sequence[AppConfig]] = ..., **kwargs: Any) -> List[Warning]: ...
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from typing import Any, List, Iterable, Optional
|
from typing import Any, List, Optional, Sequence
|
||||||
|
|
||||||
from django.core.checks.messages import Error
|
from django.core.checks.messages import Error
|
||||||
|
|
||||||
@@ -7,5 +7,7 @@ from django.apps.config import AppConfig
|
|||||||
E001: Any
|
E001: Any
|
||||||
E002: Any
|
E002: Any
|
||||||
|
|
||||||
def check_setting_app_dirs_loaders(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Error]: ...
|
def check_setting_app_dirs_loaders(app_configs: Optional[Sequence[AppConfig]] = ..., **kwargs: Any) -> List[Error]: ...
|
||||||
def check_string_if_invalid_is_string(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Error]: ...
|
def check_string_if_invalid_is_string(
|
||||||
|
app_configs: Optional[Sequence[AppConfig]] = ..., **kwargs: Any
|
||||||
|
) -> List[Error]: ...
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
from typing import Any, List
|
from typing import Any, List, Optional, Sequence
|
||||||
|
|
||||||
|
from django.apps.config import AppConfig
|
||||||
|
|
||||||
from . import Error
|
from . import Error
|
||||||
|
|
||||||
E001: Error = ...
|
E001: Error = ...
|
||||||
|
|
||||||
def check_setting_language_code(app_configs: Any, **kwargs: Any) -> List[Error]: ...
|
def check_setting_language_code(app_configs: Optional[Sequence[AppConfig]] = ..., **kwargs: Any) -> List[Error]: ...
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
from typing import Any, Callable, List, Tuple, Union, Iterable, Optional
|
from typing import Any, Callable, List, Tuple, Union, Optional, Sequence
|
||||||
|
|
||||||
from django.core.checks.messages import CheckMessage, Error, Warning
|
from django.core.checks.messages import CheckMessage, Error, Warning
|
||||||
from django.urls.resolvers import URLPattern, URLResolver
|
from django.urls.resolvers import URLPattern, URLResolver
|
||||||
|
|
||||||
from django.apps.config import AppConfig
|
from django.apps.config import AppConfig
|
||||||
|
|
||||||
def check_url_config(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[CheckMessage]: ...
|
def check_url_config(app_configs: Optional[Sequence[AppConfig]] = ..., **kwargs: Any) -> List[CheckMessage]: ...
|
||||||
def check_resolver(resolver: Union[Tuple[str, Callable], URLPattern, URLResolver]) -> List[CheckMessage]: ...
|
def check_resolver(resolver: Union[Tuple[str, Callable], URLPattern, URLResolver]) -> List[CheckMessage]: ...
|
||||||
def check_url_namespaces_unique(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Warning]: ...
|
def check_url_namespaces_unique(app_configs: Optional[Sequence[AppConfig]] = ..., **kwargs: Any) -> List[Warning]: ...
|
||||||
def get_warning_for_invalid_pattern(pattern: Any) -> List[Error]: ...
|
def get_warning_for_invalid_pattern(pattern: Any) -> List[Error]: ...
|
||||||
def check_url_settings(app_configs: Optional[Iterable[AppConfig]], **kwargs: Any) -> List[Error]: ...
|
def check_url_settings(app_configs: Optional[Sequence[AppConfig]] = ..., **kwargs: Any) -> List[Error]: ...
|
||||||
def E006(name: str) -> Error: ...
|
def E006(name: str) -> Error: ...
|
||||||
|
|||||||
8
django-stubs/core/management/commands/check.pyi
Normal file
8
django-stubs/core/management/commands/check.pyi
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
from django.apps import apps as apps
|
||||||
|
from django.core import checks as checks
|
||||||
|
from django.core.checks.registry import registry as registry
|
||||||
|
from django.core.management.base import BaseCommand as BaseCommand, CommandError as CommandError
|
||||||
|
from typing import Any, List
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
def handle(self, *app_labels: List[str], **options: Any) -> None: ...
|
||||||
20
django-stubs/core/management/commands/compilemessages.pyi
Normal file
20
django-stubs/core/management/commands/compilemessages.pyi
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import os
|
||||||
|
from django.core.management.base import (
|
||||||
|
BaseCommand as BaseCommand,
|
||||||
|
CommandError as CommandError,
|
||||||
|
CommandParser as CommandParser,
|
||||||
|
)
|
||||||
|
from django.core.management.utils import find_command as find_command, popen_wrapper as popen_wrapper
|
||||||
|
from typing import List, Tuple, Union
|
||||||
|
|
||||||
|
_PathType = Union[str, bytes, os.PathLike]
|
||||||
|
|
||||||
|
def has_bom(fn: _PathType) -> bool: ...
|
||||||
|
def is_writable(path: _PathType) -> bool: ...
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
program: str = ...
|
||||||
|
program_options: List[str] = ...
|
||||||
|
verbosity: int = ...
|
||||||
|
has_errors: bool = ...
|
||||||
|
def compile_messages(self, locations: List[Tuple[_PathType, _PathType]]) -> None: ...
|
||||||
18
django-stubs/core/management/commands/createcachetable.pyi
Normal file
18
django-stubs/core/management/commands/createcachetable.pyi
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
from django.conf import settings as settings
|
||||||
|
from django.core.cache import caches as caches
|
||||||
|
from django.core.cache.backends.db import BaseDatabaseCache as BaseDatabaseCache
|
||||||
|
from django.core.management.base import BaseCommand as BaseCommand, CommandError as CommandError
|
||||||
|
from django.db import (
|
||||||
|
DEFAULT_DB_ALIAS as DEFAULT_DB_ALIAS,
|
||||||
|
DatabaseError as DatabaseError,
|
||||||
|
connections as connections,
|
||||||
|
models as models,
|
||||||
|
router as router,
|
||||||
|
transaction as transaction,
|
||||||
|
)
|
||||||
|
from typing import Any, List
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
verbosity: int = ...
|
||||||
|
def handle(self, *tablenames: List[str], **options: Any) -> None: ...
|
||||||
|
def create_table(self, database: str, tablename: str, dry_run: bool) -> None: ...
|
||||||
8
django-stubs/core/management/commands/dbshell.pyi
Normal file
8
django-stubs/core/management/commands/dbshell.pyi
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
from django.core.management.base import (
|
||||||
|
BaseCommand as BaseCommand,
|
||||||
|
CommandError as CommandError,
|
||||||
|
CommandParser as CommandParser,
|
||||||
|
)
|
||||||
|
from django.db import DEFAULT_DB_ALIAS as DEFAULT_DB_ALIAS, connections as connections
|
||||||
|
|
||||||
|
class Command(BaseCommand): ...
|
||||||
12
django-stubs/core/management/commands/diffsettings.pyi
Normal file
12
django-stubs/core/management/commands/diffsettings.pyi
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
from django.core.management.base import BaseCommand as BaseCommand
|
||||||
|
from typing import Any, Callable, Dict, List
|
||||||
|
|
||||||
|
def module_to_dict(module: Any, omittable: Callable[[str], bool] = ...) -> Dict[str, str]: ...
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
def output_hash(
|
||||||
|
self, user_settings: Dict[str, str], default_settings: Dict[str, str], **options: Any
|
||||||
|
) -> List[str]: ...
|
||||||
|
def output_unified(
|
||||||
|
self, user_settings: Dict[str, str], default_settings: Dict[str, str], **options: Any
|
||||||
|
) -> List[str]: ...
|
||||||
10
django-stubs/core/management/commands/flush.pyi
Normal file
10
django-stubs/core/management/commands/flush.pyi
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
from django.apps import apps as apps
|
||||||
|
from django.core.management.base import BaseCommand as BaseCommand, CommandError as CommandError
|
||||||
|
from django.core.management.color import Style, no_style as no_style
|
||||||
|
from django.core.management.sql import emit_post_migrate_signal as emit_post_migrate_signal, sql_flush as sql_flush
|
||||||
|
from django.db import DEFAULT_DB_ALIAS as DEFAULT_DB_ALIAS, connections as connections
|
||||||
|
from typing import Tuple
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
stealth_options: Tuple[str] = ...
|
||||||
|
style: Style = ...
|
||||||
16
django-stubs/core/management/commands/inspectdb.pyi
Normal file
16
django-stubs/core/management/commands/inspectdb.pyi
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
from django.core.management.base import BaseCommand as BaseCommand, CommandError as CommandError
|
||||||
|
from django.db import DEFAULT_DB_ALIAS as DEFAULT_DB_ALIAS, connections as connections
|
||||||
|
from django.db.models.constants import LOOKUP_SEP as LOOKUP_SEP
|
||||||
|
from typing import Any, Dict, Iterable, List, Tuple
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
stealth_options: Tuple[str] = ...
|
||||||
|
db_module: str = ...
|
||||||
|
def handle_inspection(self, options: Dict[str, Any]) -> Iterable[str]: ...
|
||||||
|
def normalize_col_name(
|
||||||
|
self, col_name: str, used_column_names: List[str], is_relation: bool
|
||||||
|
) -> Tuple[str, Dict[str, str], List[str]]: ...
|
||||||
|
def get_field_type(self, connection: Any, table_name: Any, row: Any) -> Tuple[str, Dict[str, str], List[str]]: ...
|
||||||
|
def get_meta(
|
||||||
|
self, table_name: str, constraints: Any, column_to_field_name: Any, is_view: Any, is_partition: Any
|
||||||
|
) -> List[str]: ...
|
||||||
@@ -1,19 +1,35 @@
|
|||||||
import zipfile
|
import zipfile
|
||||||
from typing import Iterable, List, Optional, Tuple
|
from typing import List, Optional, Set, Sequence, Tuple, Type
|
||||||
|
|
||||||
|
from django.apps.config import AppConfig
|
||||||
from django.core.management.base import BaseCommand
|
from django.core.management.base import BaseCommand
|
||||||
|
from django.db.models.base import Model
|
||||||
|
|
||||||
READ_STDIN: str = ...
|
READ_STDIN: str = ...
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
|
ignore: bool = ...
|
||||||
|
using: str = ...
|
||||||
|
app_label: str = ...
|
||||||
|
verbosity: int = ...
|
||||||
|
excluded_models: Set[Type[Model]] = ...
|
||||||
|
excluded_apps: Set[AppConfig] = ...
|
||||||
|
format: str = ...
|
||||||
missing_args_message: str = ...
|
missing_args_message: str = ...
|
||||||
def loaddata(self, fixture_labels: Iterable[str]) -> None: ...
|
def loaddata(self, fixture_labels: Sequence[str]) -> None: ...
|
||||||
def load_label(self, fixture_label: str) -> None: ...
|
def load_label(self, fixture_label: str) -> None: ...
|
||||||
def find_fixtures(self, fixture_label: str) -> List[Optional[str]]: ...
|
def find_fixtures(self, fixture_label: str) -> List[Tuple[str, Optional[str], Optional[str]]]: ...
|
||||||
@property
|
@property
|
||||||
def fixture_dirs(self) -> List[str]: ...
|
def fixture_dirs(self) -> List[str]: ...
|
||||||
def parse_name(self, fixture_name: str) -> Tuple[str, str, str]: ...
|
def parse_name(self, fixture_name: str) -> Tuple[str, Optional[str], Optional[str]]: ...
|
||||||
|
|
||||||
class SingleZipReader(zipfile.ZipFile): ...
|
class SingleZipReader(zipfile.ZipFile):
|
||||||
|
# Incompatible override
|
||||||
|
# zipfile.ZipFile.read(
|
||||||
|
# self,
|
||||||
|
# name: typing.Union[typing.Text, zipfile.ZipInfo],
|
||||||
|
# pwd: Optional[bytes] = ...,
|
||||||
|
# ) -> bytes: ...
|
||||||
|
def read(self) -> bytes: ... # type: ignore[override]
|
||||||
|
|
||||||
def humanize(dirname: str) -> str: ...
|
def humanize(dirname: str) -> str: ...
|
||||||
|
|||||||
36
django-stubs/core/management/commands/makemigrations.pyi
Normal file
36
django-stubs/core/management/commands/makemigrations.pyi
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
from django.apps import apps as apps
|
||||||
|
from django.conf import settings as settings
|
||||||
|
from django.core.management.base import (
|
||||||
|
BaseCommand as BaseCommand,
|
||||||
|
CommandError as CommandError,
|
||||||
|
no_translations as no_translations,
|
||||||
|
)
|
||||||
|
from django.db import (
|
||||||
|
DEFAULT_DB_ALIAS as DEFAULT_DB_ALIAS,
|
||||||
|
OperationalError as OperationalError,
|
||||||
|
connections as connections,
|
||||||
|
router as router,
|
||||||
|
)
|
||||||
|
from django.db.migrations import Migration as Migration
|
||||||
|
from django.db.migrations.autodetector import MigrationAutodetector as MigrationAutodetector
|
||||||
|
from django.db.migrations.loader import MigrationLoader as MigrationLoader
|
||||||
|
from django.db.migrations.questioner import (
|
||||||
|
InteractiveMigrationQuestioner as InteractiveMigrationQuestioner,
|
||||||
|
MigrationQuestioner as MigrationQuestioner,
|
||||||
|
NonInteractiveMigrationQuestioner as NonInteractiveMigrationQuestioner,
|
||||||
|
)
|
||||||
|
from django.db.migrations.state import ProjectState as ProjectState
|
||||||
|
from django.db.migrations.utils import get_migration_name_timestamp as get_migration_name_timestamp
|
||||||
|
from django.db.migrations.writer import MigrationWriter as MigrationWriter
|
||||||
|
from typing import Any, Dict
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
verbosity: int = ...
|
||||||
|
interactive: bool = ...
|
||||||
|
dry_run: bool = ...
|
||||||
|
merge: bool = ...
|
||||||
|
empty: bool = ...
|
||||||
|
migration_name: str = ...
|
||||||
|
include_header: bool = ...
|
||||||
|
def write_migration_files(self, changes: Dict[str, Any]) -> None: ...
|
||||||
|
def handle_merge(self, loader: MigrationLoader, conflicts: Dict[str, Any]) -> None: ...
|
||||||
28
django-stubs/core/management/commands/migrate.pyi
Normal file
28
django-stubs/core/management/commands/migrate.pyi
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
from django.apps import apps as apps
|
||||||
|
from django.core.management.base import (
|
||||||
|
BaseCommand as BaseCommand,
|
||||||
|
CommandError as CommandError,
|
||||||
|
no_translations as no_translations,
|
||||||
|
)
|
||||||
|
from django.core.management.sql import (
|
||||||
|
emit_post_migrate_signal as emit_post_migrate_signal,
|
||||||
|
emit_pre_migrate_signal as emit_pre_migrate_signal,
|
||||||
|
)
|
||||||
|
from django.db import DEFAULT_DB_ALIAS as DEFAULT_DB_ALIAS, connections as connections, router as router
|
||||||
|
from django.db.migrations.autodetector import MigrationAutodetector as MigrationAutodetector
|
||||||
|
from django.db.migrations.executor import MigrationExecutor as MigrationExecutor
|
||||||
|
from django.db.migrations.loader import AmbiguityError as AmbiguityError
|
||||||
|
from django.db.migrations.operations.base import Operation
|
||||||
|
from django.db.migrations.state import ModelState as ModelState, ProjectState as ProjectState
|
||||||
|
from django.utils.module_loading import module_has_submodule as module_has_submodule
|
||||||
|
from django.utils.text import Truncator as Truncator
|
||||||
|
from typing import Any, List, Optional
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
verbosity: int = ...
|
||||||
|
interactive: bool = ...
|
||||||
|
start: float = ...
|
||||||
|
def migration_progress_callback(self, action: str, migration: Optional[Any] = ..., fake: bool = ...) -> None: ...
|
||||||
|
def sync_apps(self, connection: Any, app_labels: List[str]) -> None: ...
|
||||||
|
@staticmethod
|
||||||
|
def describe_operation(operation: Operation, backwards: bool) -> str: ...
|
||||||
6
django-stubs/core/management/commands/sendtestemail.pyi
Normal file
6
django-stubs/core/management/commands/sendtestemail.pyi
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
from django.core.mail import mail_admins as mail_admins, mail_managers as mail_managers, send_mail as send_mail
|
||||||
|
from django.core.management.base import BaseCommand as BaseCommand
|
||||||
|
from django.utils import timezone as timezone
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
missing_args_message: str = ...
|
||||||
9
django-stubs/core/management/commands/shell.pyi
Normal file
9
django-stubs/core/management/commands/shell.pyi
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
from django.core.management import BaseCommand as BaseCommand, CommandError as CommandError
|
||||||
|
from django.utils.datastructures import OrderedSet as OrderedSet
|
||||||
|
from typing import Any, List
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
shells: List[str] = ...
|
||||||
|
def ipython(self, options: Any) -> None: ...
|
||||||
|
def bpython(self, options: Any) -> None: ...
|
||||||
|
def python(self, options: Any) -> None: ...
|
||||||
10
django-stubs/core/management/commands/showmigrations.pyi
Normal file
10
django-stubs/core/management/commands/showmigrations.pyi
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
from django.apps import apps as apps
|
||||||
|
from django.core.management.base import BaseCommand as BaseCommand
|
||||||
|
from django.db import DEFAULT_DB_ALIAS as DEFAULT_DB_ALIAS, connections as connections
|
||||||
|
from django.db.migrations.loader import MigrationLoader as MigrationLoader
|
||||||
|
from typing import Any, List, Optional
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
verbosity: int = ...
|
||||||
|
def show_list(self, connection: Any, app_names: Optional[List[str]] = ...) -> None: ...
|
||||||
|
def show_plan(self, connection: Any, app_names: Optional[List[str]] = ...) -> None: ...
|
||||||
6
django-stubs/core/management/commands/sqlflush.pyi
Normal file
6
django-stubs/core/management/commands/sqlflush.pyi
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
from django.core.management.base import BaseCommand as BaseCommand
|
||||||
|
from django.core.management.sql import sql_flush as sql_flush
|
||||||
|
from django.db import DEFAULT_DB_ALIAS as DEFAULT_DB_ALIAS, connections as connections
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
output_transaction: bool = ...
|
||||||
9
django-stubs/core/management/commands/sqlmigrate.pyi
Normal file
9
django-stubs/core/management/commands/sqlmigrate.pyi
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
from django.apps import apps as apps
|
||||||
|
from django.core.management.base import BaseCommand as BaseCommand, CommandError as CommandError
|
||||||
|
from django.db import DEFAULT_DB_ALIAS as DEFAULT_DB_ALIAS, connections as connections
|
||||||
|
from django.db.migrations.loader import AmbiguityError as AmbiguityError, MigrationLoader as MigrationLoader
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
output_transaction: bool = ...
|
||||||
|
def execute(self, *args: Any, **options: Any): ...
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
from django.core.management.base import AppCommand as AppCommand
|
||||||
|
from django.db import DEFAULT_DB_ALIAS as DEFAULT_DB_ALIAS, connections as connections
|
||||||
|
|
||||||
|
class Command(AppCommand): ...
|
||||||
15
django-stubs/core/management/commands/squashmigrations.pyi
Normal file
15
django-stubs/core/management/commands/squashmigrations.pyi
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
from django.apps import apps as apps
|
||||||
|
from django.conf import settings as settings
|
||||||
|
from django.core.management.base import BaseCommand as BaseCommand, CommandError as CommandError
|
||||||
|
from django.db import DEFAULT_DB_ALIAS as DEFAULT_DB_ALIAS, connections as connections, migrations as migrations
|
||||||
|
from django.db.migrations.migration import Migration
|
||||||
|
from django.db.migrations.loader import AmbiguityError as AmbiguityError, MigrationLoader as MigrationLoader
|
||||||
|
from django.db.migrations.migration import SwappableTuple as SwappableTuple
|
||||||
|
from django.db.migrations.optimizer import MigrationOptimizer as MigrationOptimizer
|
||||||
|
from django.db.migrations.writer import MigrationWriter as MigrationWriter
|
||||||
|
from django.utils.version import get_docs_version as get_docs_version
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
verbosity: int = ...
|
||||||
|
interactive: bool = ...
|
||||||
|
def find_migration(self, loader: MigrationLoader, app_label: str, name: str) -> Migration: ...
|
||||||
5
django-stubs/core/management/commands/startapp.pyi
Normal file
5
django-stubs/core/management/commands/startapp.pyi
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
from django.core.management.templates import TemplateCommand as TemplateCommand
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
class Command(TemplateCommand):
|
||||||
|
missing_args_message: str = ...
|
||||||
5
django-stubs/core/management/commands/startproject.pyi
Normal file
5
django-stubs/core/management/commands/startproject.pyi
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
from ..utils import get_random_secret_key as get_random_secret_key
|
||||||
|
from django.core.management.templates import TemplateCommand as TemplateCommand
|
||||||
|
|
||||||
|
class Command(TemplateCommand):
|
||||||
|
missing_args_message: str = ...
|
||||||
10
django-stubs/core/management/commands/test.pyi
Normal file
10
django-stubs/core/management/commands/test.pyi
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
from django.conf import settings as settings
|
||||||
|
from django.core.management.base import BaseCommand as BaseCommand
|
||||||
|
from django.core.management.utils import get_command_line_option as get_command_line_option
|
||||||
|
from django.test.utils import get_runner as get_runner
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
test_runner: Any = ...
|
||||||
|
def run_from_argv(self, argv: Any) -> None: ...
|
||||||
|
def add_arguments(self, parser: Any) -> None: ...
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
from typing import List, Optional, Set, Tuple, Type
|
from typing import Any, List, Optional, Set, Tuple, Type
|
||||||
|
|
||||||
from django.apps.config import AppConfig
|
from django.apps.config import AppConfig
|
||||||
from django.db.models.base import Model
|
from django.db.models.base import Model
|
||||||
@@ -8,3 +8,4 @@ def handle_extensions(extensions: List[str]) -> Set[str]: ...
|
|||||||
def find_command(cmd: str, path: Optional[str] = ..., pathext: Optional[str] = ...) -> Optional[str]: ...
|
def find_command(cmd: str, path: Optional[str] = ..., pathext: Optional[str] = ...) -> Optional[str]: ...
|
||||||
def get_random_secret_key(): ...
|
def get_random_secret_key(): ...
|
||||||
def parse_apps_and_model_labels(labels: List[str]) -> Tuple[Set[Type[Model]], Set[AppConfig]]: ...
|
def parse_apps_and_model_labels(labels: List[str]) -> Tuple[Set[Type[Model]], Set[AppConfig]]: ...
|
||||||
|
def get_command_line_option(argv: Any, option: Any): ...
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ class SignatureExpired(BadSignature): ...
|
|||||||
|
|
||||||
def b64_encode(s: bytes) -> bytes: ...
|
def b64_encode(s: bytes) -> bytes: ...
|
||||||
def b64_decode(s: bytes) -> bytes: ...
|
def b64_decode(s: bytes) -> bytes: ...
|
||||||
def base64_hmac(salt: str, value: Union[bytes, str], key: Union[bytes, str]) -> str: ...
|
def base64_hmac(salt: str, value: Union[bytes, str], key: Union[bytes, str], algorithm: str = ...) -> str: ...
|
||||||
def get_cookie_signer(salt: str = ...) -> TimestampSigner: ...
|
def get_cookie_signer(salt: str = ...) -> TimestampSigner: ...
|
||||||
|
|
||||||
class Serializer(Protocol):
|
class Serializer(Protocol):
|
||||||
@@ -32,7 +32,14 @@ class Signer:
|
|||||||
key: str = ...
|
key: str = ...
|
||||||
sep: str = ...
|
sep: str = ...
|
||||||
salt: str = ...
|
salt: str = ...
|
||||||
def __init__(self, key: Optional[Union[bytes, str]] = ..., sep: str = ..., salt: Optional[str] = ...) -> None: ...
|
algorithm: str = ...
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
key: Optional[Union[bytes, str]] = ...,
|
||||||
|
sep: str = ...,
|
||||||
|
salt: Optional[str] = ...,
|
||||||
|
algorithm: Optional[str] = ...,
|
||||||
|
) -> None: ...
|
||||||
def signature(self, value: Union[bytes, str]) -> str: ...
|
def signature(self, value: Union[bytes, str]) -> str: ...
|
||||||
def sign(self, value: str) -> str: ...
|
def sign(self, value: str) -> str: ...
|
||||||
def unsign(self, signed_value: str) -> str: ...
|
def unsign(self, signed_value: str) -> str: ...
|
||||||
|
|||||||
@@ -3,6 +3,13 @@ from typing import Any, Dict, Optional, Sequence, Set, Tuple, Union
|
|||||||
from django.db.migrations.migration import Migration
|
from django.db.migrations.migration import Migration
|
||||||
from django.db.migrations.state import ProjectState
|
from django.db.migrations.state import ProjectState
|
||||||
|
|
||||||
|
from .exceptions import (
|
||||||
|
AmbiguityError as AmbiguityError,
|
||||||
|
BadMigrationError as BadMigrationError,
|
||||||
|
InconsistentMigrationHistory as InconsistentMigrationHistory,
|
||||||
|
NodeNotFoundError as NodeNotFoundError,
|
||||||
|
)
|
||||||
|
|
||||||
MIGRATIONS_MODULE_NAME: str
|
MIGRATIONS_MODULE_NAME: str
|
||||||
|
|
||||||
class MigrationLoader:
|
class MigrationLoader:
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ class Operation:
|
|||||||
def state_forwards(self, app_label: Any, state: Any) -> None: ...
|
def state_forwards(self, app_label: Any, state: Any) -> None: ...
|
||||||
def database_forwards(self, app_label: Any, schema_editor: Any, from_state: Any, to_state: Any) -> None: ...
|
def database_forwards(self, app_label: Any, schema_editor: Any, from_state: Any, to_state: Any) -> None: ...
|
||||||
def database_backwards(self, app_label: Any, schema_editor: Any, from_state: Any, to_state: Any) -> None: ...
|
def database_backwards(self, app_label: Any, schema_editor: Any, from_state: Any, to_state: Any) -> None: ...
|
||||||
def describe(self): ...
|
def describe(self) -> str: ...
|
||||||
def references_model(self, name: str, app_label: str = ...) -> bool: ...
|
def references_model(self, name: str, app_label: str = ...) -> bool: ...
|
||||||
def references_field(self, model_name: str, name: str, app_label: str = ...) -> bool: ...
|
def references_field(self, model_name: str, name: str, app_label: str = ...) -> bool: ...
|
||||||
def allow_migrate_model(self, connection_alias: Any, model: Any): ...
|
def allow_migrate_model(self, connection_alias: Any, model: Any) -> bool: ...
|
||||||
def reduce(self, operation: Operation, in_between: List[Operation], app_label: str = ...) -> bool: ...
|
def reduce(self, operation: Operation, in_between: List[Operation], app_label: str = ...) -> bool: ...
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ from .fields import (
|
|||||||
IntegerField as IntegerField,
|
IntegerField as IntegerField,
|
||||||
PositiveIntegerField as PositiveIntegerField,
|
PositiveIntegerField as PositiveIntegerField,
|
||||||
PositiveSmallIntegerField as PositiveSmallIntegerField,
|
PositiveSmallIntegerField as PositiveSmallIntegerField,
|
||||||
|
PositiveBigIntegerField as PositiveBigIntegerField,
|
||||||
SmallIntegerField as SmallIntegerField,
|
SmallIntegerField as SmallIntegerField,
|
||||||
BigIntegerField as BigIntegerField,
|
BigIntegerField as BigIntegerField,
|
||||||
FloatField as FloatField,
|
FloatField as FloatField,
|
||||||
@@ -60,6 +61,7 @@ from .fields.files import (
|
|||||||
FileDescriptor as FileDescriptor,
|
FileDescriptor as FileDescriptor,
|
||||||
)
|
)
|
||||||
from .fields.proxy import OrderWrt as OrderWrt
|
from .fields.proxy import OrderWrt as OrderWrt
|
||||||
|
from .fields.json import JSONField as JSONField
|
||||||
|
|
||||||
from .deletion import (
|
from .deletion import (
|
||||||
CASCADE as CASCADE,
|
CASCADE as CASCADE,
|
||||||
@@ -130,6 +132,7 @@ from . import signals as signals
|
|||||||
from .constraints import (
|
from .constraints import (
|
||||||
BaseConstraint as BaseConstraint,
|
BaseConstraint as BaseConstraint,
|
||||||
CheckConstraint as CheckConstraint,
|
CheckConstraint as CheckConstraint,
|
||||||
|
Deferrable as Deferrable,
|
||||||
UniqueConstraint as UniqueConstraint,
|
UniqueConstraint as UniqueConstraint,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from typing import Any, Callable, Collection, Dict, Iterable, List, Optional, Set, Tuple, Type, TypeVar, Union
|
from typing import Any, Callable, Collection, Dict, Iterable, List, Optional, Set, Tuple, Type, TypeVar, Union, ClassVar
|
||||||
|
|
||||||
from django.core.checks.messages import CheckMessage
|
from django.core.checks.messages import CheckMessage
|
||||||
from django.core.exceptions import (
|
from django.core.exceptions import (
|
||||||
@@ -26,7 +26,7 @@ class Model(metaclass=ModelBase):
|
|||||||
class Meta: ...
|
class Meta: ...
|
||||||
_meta: Options[Any]
|
_meta: Options[Any]
|
||||||
_default_manager: BaseManager[Model]
|
_default_manager: BaseManager[Model]
|
||||||
objects: BaseManager[Any]
|
objects: ClassVar[BaseManager[Any]]
|
||||||
pk: Any = ...
|
pk: Any = ...
|
||||||
_state: ModelState
|
_state: ModelState
|
||||||
def __init__(self: _Self, *args, **kwargs) -> None: ...
|
def __init__(self: _Self, *args, **kwargs) -> None: ...
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import enum
|
||||||
from typing import Any, Optional, Sequence, Tuple, Type, TypeVar
|
from typing import Any, Optional, Sequence, Tuple, Type, TypeVar
|
||||||
|
|
||||||
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
|
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
|
||||||
@@ -6,6 +7,10 @@ from django.db.models.query_utils import Q
|
|||||||
|
|
||||||
_T = TypeVar("_T", bound="BaseConstraint")
|
_T = TypeVar("_T", bound="BaseConstraint")
|
||||||
|
|
||||||
|
class Deferrable(enum.Enum):
|
||||||
|
DEFERRED: str
|
||||||
|
IMMEDIATE: str
|
||||||
|
|
||||||
class BaseConstraint:
|
class BaseConstraint:
|
||||||
name: str
|
name: str
|
||||||
def __init__(self, name: str) -> None: ...
|
def __init__(self, name: str) -> None: ...
|
||||||
@@ -24,4 +29,7 @@ class CheckConstraint(BaseConstraint):
|
|||||||
class UniqueConstraint(BaseConstraint):
|
class UniqueConstraint(BaseConstraint):
|
||||||
fields: Tuple[str]
|
fields: Tuple[str]
|
||||||
condition: Optional[Q]
|
condition: Optional[Q]
|
||||||
def __init__(self, *, fields: Sequence[str], name: str, condition: Optional[Q] = ...): ...
|
deferrable: Optional[Deferrable]
|
||||||
|
def __init__(
|
||||||
|
self, *, fields: Sequence[str], name: str, condition: Optional[Q] = ..., deferrable: Optional[Deferrable] = ...
|
||||||
|
) -> None: ...
|
||||||
|
|||||||
@@ -142,6 +142,7 @@ class Field(RegisterLookupMixin, Generic[_ST, _GT]):
|
|||||||
def cached_col(self) -> Col: ...
|
def cached_col(self) -> Col: ...
|
||||||
def value_from_object(self, obj: Model) -> _GT: ...
|
def value_from_object(self, obj: Model) -> _GT: ...
|
||||||
def get_attname(self) -> str: ...
|
def get_attname(self) -> str: ...
|
||||||
|
def value_to_string(self, obj: Model) -> str: ...
|
||||||
|
|
||||||
class IntegerField(Field[_ST, _GT]):
|
class IntegerField(Field[_ST, _GT]):
|
||||||
_pyi_private_set_type: Union[float, int, str, Combinable]
|
_pyi_private_set_type: Union[float, int, str, Combinable]
|
||||||
@@ -153,6 +154,7 @@ class PositiveIntegerRelDbTypeMixin:
|
|||||||
|
|
||||||
class PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField[_ST, _GT]): ...
|
class PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField[_ST, _GT]): ...
|
||||||
class PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField[_ST, _GT]): ...
|
class PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField[_ST, _GT]): ...
|
||||||
|
class PositiveBigIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField[_ST, _GT]): ...
|
||||||
class SmallIntegerField(IntegerField[_ST, _GT]): ...
|
class SmallIntegerField(IntegerField[_ST, _GT]): ...
|
||||||
class BigIntegerField(IntegerField[_ST, _GT]): ...
|
class BigIntegerField(IntegerField[_ST, _GT]): ...
|
||||||
|
|
||||||
|
|||||||
@@ -38,10 +38,10 @@ class FileField(Field):
|
|||||||
upload_to: Union[str, Callable] = ...
|
upload_to: Union[str, Callable] = ...
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
upload_to: Union[str, Callable, Path] = ...,
|
|
||||||
storage: Optional[Union[Storage, Callable[[], Storage]]] = ...,
|
|
||||||
verbose_name: Optional[Union[str, bytes]] = ...,
|
verbose_name: Optional[Union[str, bytes]] = ...,
|
||||||
name: Optional[str] = ...,
|
name: Optional[str] = ...,
|
||||||
|
upload_to: Union[str, Callable, Path] = ...,
|
||||||
|
storage: Optional[Union[Storage, Callable[[], Storage]]] = ...,
|
||||||
max_length: Optional[int] = ...,
|
max_length: Optional[int] = ...,
|
||||||
unique: bool = ...,
|
unique: bool = ...,
|
||||||
blank: bool = ...,
|
blank: bool = ...,
|
||||||
|
|||||||
@@ -2,103 +2,42 @@ from . import Field
|
|||||||
from .mixins import CheckFieldDefaultMixin
|
from .mixins import CheckFieldDefaultMixin
|
||||||
from django.db.models import lookups
|
from django.db.models import lookups
|
||||||
from django.db.models.lookups import PostgresOperatorLookup, Transform
|
from django.db.models.lookups import PostgresOperatorLookup, Transform
|
||||||
from typing import Any, Optional
|
from typing import Any, Optional, Callable
|
||||||
|
|
||||||
|
|
||||||
class JSONField(CheckFieldDefaultMixin, Field):
|
class JSONField(CheckFieldDefaultMixin, Field):
|
||||||
empty_strings_allowed: bool = ...
|
|
||||||
description: Any = ...
|
|
||||||
default_error_messages: Any = ...
|
|
||||||
encoder: Any = ...
|
|
||||||
decoder: Any = ...
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
verbose_name: Optional[Any] = ...,
|
verbose_name: Optional[str] = ...,
|
||||||
name: Optional[Any] = ...,
|
name: Optional[str] = ...,
|
||||||
encoder: Optional[Any] = ...,
|
encoder: Optional[Callable] = ...,
|
||||||
decoder: Optional[Any] = ...,
|
decoder: Optional[Callable] = ...,
|
||||||
**kwargs: Any
|
**kwargs: Any
|
||||||
) -> None: ...
|
) -> None: ...
|
||||||
def check(self, **kwargs: Any): ...
|
|
||||||
def deconstruct(self): ...
|
|
||||||
def from_db_value(self, value: Any, expression: Any, connection: Any): ...
|
|
||||||
def get_internal_type(self): ...
|
|
||||||
def get_prep_value(self, value: Any): ...
|
|
||||||
def get_transform(self, name: Any): ...
|
|
||||||
def validate(self, value: Any, model_instance: Any) -> None: ...
|
|
||||||
def value_to_string(self, obj: Any): ...
|
|
||||||
def formfield(self, **kwargs: Any): ...
|
|
||||||
|
|
||||||
class DataContains(PostgresOperatorLookup):
|
class DataContains(PostgresOperatorLookup): ...
|
||||||
lookup_name: str = ...
|
class ContainedBy(PostgresOperatorLookup): ...
|
||||||
postgres_operator: str = ...
|
|
||||||
def as_sql(self, compiler: Any, connection: Any): ...
|
|
||||||
|
|
||||||
class ContainedBy(PostgresOperatorLookup):
|
class HasKeyLookup(PostgresOperatorLookup): ...
|
||||||
lookup_name: str = ...
|
class HasKey(HasKeyLookup): ...
|
||||||
postgres_operator: str = ...
|
class HasKeys(HasKeyLookup): ...
|
||||||
def as_sql(self, compiler: Any, connection: Any): ...
|
class HasAnyKeys(HasKeys): ...
|
||||||
|
class JSONExact(lookups.Exact): ...
|
||||||
class HasKeyLookup(PostgresOperatorLookup):
|
|
||||||
logical_operator: Any = ...
|
|
||||||
def as_sql(self, compiler: Any, connection: Any, template: Optional[Any] = ...): ...
|
|
||||||
def as_mysql(self, compiler: Any, connection: Any): ...
|
|
||||||
def as_oracle(self, compiler: Any, connection: Any): ...
|
|
||||||
lhs: Any = ...
|
|
||||||
rhs: Any = ...
|
|
||||||
def as_postgresql(self, compiler: Any, connection: Any): ...
|
|
||||||
def as_sqlite(self, compiler: Any, connection: Any): ...
|
|
||||||
|
|
||||||
class HasKey(HasKeyLookup):
|
|
||||||
lookup_name: str = ...
|
|
||||||
postgres_operator: str = ...
|
|
||||||
prepare_rhs: bool = ...
|
|
||||||
|
|
||||||
class HasKeys(HasKeyLookup):
|
|
||||||
lookup_name: str = ...
|
|
||||||
postgres_operator: str = ...
|
|
||||||
logical_operator: str = ...
|
|
||||||
def get_prep_lookup(self): ...
|
|
||||||
|
|
||||||
class HasAnyKeys(HasKeys):
|
|
||||||
lookup_name: str = ...
|
|
||||||
postgres_operator: str = ...
|
|
||||||
logical_operator: str = ...
|
|
||||||
|
|
||||||
class JSONExact(lookups.Exact):
|
|
||||||
can_use_none_as_rhs: bool = ...
|
|
||||||
def process_rhs(self, compiler: Any, connection: Any): ...
|
|
||||||
|
|
||||||
class KeyTransform(Transform):
|
class KeyTransform(Transform):
|
||||||
postgres_operator: str = ...
|
|
||||||
postgres_nested_operator: str = ...
|
|
||||||
key_name: Any = ...
|
key_name: Any = ...
|
||||||
def __init__(self, key_name: Any, *args: Any, **kwargs: Any) -> None: ...
|
def __init__(self, key_name: Any, *args: Any, **kwargs: Any) -> None: ...
|
||||||
def preprocess_lhs(self, compiler: Any, connection: Any, lhs_only: bool = ...): ...
|
def preprocess_lhs(self, compiler: Any, connection: Any, lhs_only: bool = ...): ...
|
||||||
def as_mysql(self, compiler: Any, connection: Any): ...
|
|
||||||
def as_oracle(self, compiler: Any, connection: Any): ...
|
|
||||||
def as_postgresql(self, compiler: Any, connection: Any): ...
|
|
||||||
|
|
||||||
class KeyTextTransform(KeyTransform):
|
|
||||||
postgres_operator: str = ...
|
|
||||||
postgres_nested_operator: str = ...
|
|
||||||
|
|
||||||
|
class KeyTextTransform(KeyTransform): ...
|
||||||
class KeyTransformTextLookupMixin:
|
class KeyTransformTextLookupMixin:
|
||||||
def __init__(self, key_transform: Any, *args: Any, **kwargs: Any) -> None: ...
|
def __init__(self, key_transform: Any, *args: Any, **kwargs: Any) -> None: ...
|
||||||
|
|
||||||
class CaseInsensitiveMixin:
|
class CaseInsensitiveMixin: ...
|
||||||
def process_rhs(self, compiler: Any, connection: Any): ...
|
class KeyTransformIsNull(lookups.IsNull): ...
|
||||||
|
class KeyTransformIn(lookups.In): ...
|
||||||
class KeyTransformIsNull(lookups.IsNull):
|
|
||||||
def as_oracle(self, compiler: Any, connection: Any): ...
|
|
||||||
def as_sqlite(self, compiler: Any, connection: Any): ...
|
|
||||||
|
|
||||||
class KeyTransformIn(lookups.In):
|
|
||||||
def process_rhs(self, compiler: Any, connection: Any): ...
|
|
||||||
|
|
||||||
class KeyTransformExact(JSONExact):
|
|
||||||
def process_rhs(self, compiler: Any, connection: Any): ...
|
|
||||||
def as_oracle(self, compiler: Any, connection: Any): ...
|
|
||||||
|
|
||||||
|
class KeyTransformExact(JSONExact): ...
|
||||||
class KeyTransformIExact(CaseInsensitiveMixin, KeyTransformTextLookupMixin, lookups.IExact): ...
|
class KeyTransformIExact(CaseInsensitiveMixin, KeyTransformTextLookupMixin, lookups.IExact): ...
|
||||||
class KeyTransformIContains(CaseInsensitiveMixin, KeyTransformTextLookupMixin, lookups.IContains): ...
|
class KeyTransformIContains(CaseInsensitiveMixin, KeyTransformTextLookupMixin, lookups.IContains): ...
|
||||||
class KeyTransformStartsWith(KeyTransformTextLookupMixin, lookups.StartsWith): ...
|
class KeyTransformStartsWith(KeyTransformTextLookupMixin, lookups.StartsWith): ...
|
||||||
@@ -108,9 +47,7 @@ class KeyTransformIEndsWith(CaseInsensitiveMixin, KeyTransformTextLookupMixin, l
|
|||||||
class KeyTransformRegex(KeyTransformTextLookupMixin, lookups.Regex): ...
|
class KeyTransformRegex(KeyTransformTextLookupMixin, lookups.Regex): ...
|
||||||
class KeyTransformIRegex(CaseInsensitiveMixin, KeyTransformTextLookupMixin, lookups.IRegex): ...
|
class KeyTransformIRegex(CaseInsensitiveMixin, KeyTransformTextLookupMixin, lookups.IRegex): ...
|
||||||
|
|
||||||
class KeyTransformNumericLookupMixin:
|
class KeyTransformNumericLookupMixin: ...
|
||||||
def process_rhs(self, compiler: Any, connection: Any): ...
|
|
||||||
|
|
||||||
class KeyTransformLt(KeyTransformNumericLookupMixin, lookups.LessThan): ...
|
class KeyTransformLt(KeyTransformNumericLookupMixin, lookups.LessThan): ...
|
||||||
class KeyTransformLte(KeyTransformNumericLookupMixin, lookups.LessThanOrEqual): ...
|
class KeyTransformLte(KeyTransformNumericLookupMixin, lookups.LessThanOrEqual): ...
|
||||||
class KeyTransformGt(KeyTransformNumericLookupMixin, lookups.GreaterThan): ...
|
class KeyTransformGt(KeyTransformNumericLookupMixin, lookups.GreaterThan): ...
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ class FieldGetDbPrepValueIterableMixin(FieldGetDbPrepValueMixin):
|
|||||||
) -> Any: ...
|
) -> Any: ...
|
||||||
|
|
||||||
class PostgresOperatorLookup(FieldGetDbPrepValueMixin, Lookup):
|
class PostgresOperatorLookup(FieldGetDbPrepValueMixin, Lookup):
|
||||||
postgres_operator: Any = ...
|
postgres_operator: str = ...
|
||||||
def as_postgresql(self, compiler: Any, connection: Any): ...
|
def as_postgresql(self, compiler: Any, connection: Any): ...
|
||||||
|
|
||||||
class Exact(FieldGetDbPrepValueMixin, BuiltinLookup): ...
|
class Exact(FieldGetDbPrepValueMixin, BuiltinLookup): ...
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
from typing import Tuple, Type, Union
|
from typing import Any, Generator, MutableMapping, Tuple, Type, Union
|
||||||
|
|
||||||
from django.db.models.base import Model
|
from django.db.models.base import Model
|
||||||
|
|
||||||
def make_model_tuple(model: Union[Type[Model], str]) -> Tuple[str, str]: ...
|
def make_model_tuple(model: Union[Type[Model], str]) -> Tuple[str, str]: ...
|
||||||
|
def resolve_callables(mapping: MutableMapping[str, Any]) -> Generator[Tuple[str, Any], None, None]: ...
|
||||||
|
|||||||
@@ -255,7 +255,7 @@ class ModelChoiceField(ChoiceField):
|
|||||||
to_field_name: None = ...
|
to_field_name: None = ...
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
queryset: Optional[Union[Manager, QuerySet]],
|
queryset: Optional[Union[Manager, _BaseQuerySet]],
|
||||||
*,
|
*,
|
||||||
empty_label: Optional[str] = ...,
|
empty_label: Optional[str] = ...,
|
||||||
required: bool = ...,
|
required: bool = ...,
|
||||||
@@ -282,7 +282,7 @@ class ModelMultipleChoiceField(ModelChoiceField):
|
|||||||
widget: Any = ...
|
widget: Any = ...
|
||||||
hidden_widget: Any = ...
|
hidden_widget: Any = ...
|
||||||
default_error_messages: Any = ...
|
default_error_messages: Any = ...
|
||||||
def __init__(self, queryset: _BaseQuerySet, **kwargs: Any) -> None: ...
|
def __init__(self, queryset: Optional[Union[Manager, _BaseQuerySet]], **kwargs: Any) -> None: ...
|
||||||
|
|
||||||
def _get_foreign_key(
|
def _get_foreign_key(
|
||||||
parent_model: Type[Model], model: Type[Model], fk_name: Optional[str] = ..., can_fail: bool = ...
|
parent_model: Type[Model], model: Type[Model], fk_name: Optional[str] = ..., can_fail: bool = ...
|
||||||
|
|||||||
@@ -71,6 +71,8 @@ class HiddenInput(Input):
|
|||||||
class MultipleHiddenInput(HiddenInput): ...
|
class MultipleHiddenInput(HiddenInput): ...
|
||||||
class FileInput(Input): ...
|
class FileInput(Input): ...
|
||||||
|
|
||||||
|
FILE_INPUT_CONTRADICTION: Any
|
||||||
|
|
||||||
class ClearableFileInput(FileInput):
|
class ClearableFileInput(FileInput):
|
||||||
clear_checkbox_label: Any = ...
|
clear_checkbox_label: Any = ...
|
||||||
initial_text: Any = ...
|
initial_text: Any = ...
|
||||||
|
|||||||
@@ -6,24 +6,35 @@ from typing import Any, Dict, Iterable, Iterator, List, Optional, Tuple, Type, U
|
|||||||
from django.core.handlers.wsgi import WSGIRequest
|
from django.core.handlers.wsgi import WSGIRequest
|
||||||
from django.http.cookie import SimpleCookie
|
from django.http.cookie import SimpleCookie
|
||||||
from django.test.client import Client
|
from django.test.client import Client
|
||||||
|
from django.utils.datastructures import CaseInsensitiveMapping
|
||||||
from django.template import Context, Template
|
from django.template import Context, Template
|
||||||
from django.urls import ResolverMatch
|
from django.urls import ResolverMatch
|
||||||
|
|
||||||
class BadHeaderError(ValueError): ...
|
class BadHeaderError(ValueError): ...
|
||||||
|
|
||||||
|
class ResponseHeaders(CaseInsensitiveMapping):
|
||||||
|
store: Dict[str, str] = ...
|
||||||
|
def __init__(self, data: Dict[str, str]) -> None: ...
|
||||||
|
def _convert_to_charset(self, value: Union[bytes, str], charset: str, mime_encode: str=...) -> str: ...
|
||||||
|
def __delitem__(self, key: str) -> None: ...
|
||||||
|
def __setitem__(self, key: str, value: str) -> None: ...
|
||||||
|
def pop(self, key: str, default: Optional[str] = ...) -> str: ...
|
||||||
|
def setdefault(self, key: str, value: str) -> None: ...
|
||||||
|
|
||||||
class HttpResponseBase(Iterable[Any]):
|
class HttpResponseBase(Iterable[Any]):
|
||||||
status_code: int = ...
|
status_code: int = ...
|
||||||
cookies: SimpleCookie = ...
|
cookies: SimpleCookie = ...
|
||||||
reason_phrase: str = ...
|
reason_phrase: str = ...
|
||||||
charset: str = ...
|
charset: str = ...
|
||||||
closed: bool = ...
|
closed: bool = ...
|
||||||
|
headers: ResponseHeaders = ...
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
content_type: Optional[str] = ...,
|
content_type: Optional[str] = ...,
|
||||||
status: Optional[int] = ...,
|
status: Optional[int] = ...,
|
||||||
reason: Optional[str] = ...,
|
reason: Optional[str] = ...,
|
||||||
charset: Optional[str] = ...,
|
charset: Optional[str] = ...,
|
||||||
|
headers: Optional[Dict[str, str]] = ...,
|
||||||
) -> None: ...
|
) -> None: ...
|
||||||
def serialize_headers(self) -> bytes: ...
|
def serialize_headers(self) -> bytes: ...
|
||||||
def __setitem__(self, header: Union[str, bytes], value: Union[str, bytes, int]) -> None: ...
|
def __setitem__(self, header: Union[str, bytes], value: Union[str, bytes, int]) -> None: ...
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from typing import Any, Callable, Dict, List, Optional, Tuple, Union
|
from typing import Any, Callable, Dict, List, Optional, Tuple, TypeVar, Union, overload
|
||||||
|
|
||||||
from django.template.base import FilterExpression, Parser, Origin, Token
|
from django.template.base import FilterExpression, Parser, Origin, Token
|
||||||
from django.template.context import Context
|
from django.template.context import Context
|
||||||
@@ -8,31 +8,38 @@ from .base import Node, Template
|
|||||||
|
|
||||||
class InvalidTemplateLibrary(Exception): ...
|
class InvalidTemplateLibrary(Exception): ...
|
||||||
|
|
||||||
|
_C = TypeVar("_C", bound=Callable[..., Any])
|
||||||
|
|
||||||
class Library:
|
class Library:
|
||||||
filters: Dict[str, Callable] = ...
|
filters: Dict[str, Callable] = ...
|
||||||
tags: Dict[str, Callable] = ...
|
tags: Dict[str, Callable] = ...
|
||||||
def __init__(self) -> None: ...
|
def __init__(self) -> None: ...
|
||||||
def tag(
|
@overload
|
||||||
self, name: Optional[Union[Callable, str]] = ..., compile_function: Optional[Union[Callable, str]] = ...
|
def tag(self, name: _C) -> _C: ...
|
||||||
) -> Callable: ...
|
@overload
|
||||||
def tag_function(self, func: Callable) -> Callable: ...
|
def tag(self, name: str, compile_function: _C) -> _C: ...
|
||||||
def filter(
|
@overload
|
||||||
self,
|
def tag(self, name: Optional[str] = ..., compile_function: None = ...) -> Callable[[_C], _C]: ...
|
||||||
name: Optional[Union[Callable, str]] = ...,
|
def tag_function(self, func: _C) -> _C: ...
|
||||||
filter_func: Optional[Union[Callable, str]] = ...,
|
@overload
|
||||||
**flags: Any
|
def filter(self, name: _C, filter_func: None = ..., **flags: Any) -> _C: ...
|
||||||
) -> Callable: ...
|
@overload
|
||||||
def filter_function(self, func: Callable, **flags: Any) -> Callable: ...
|
def filter(self, name: Optional[str], filter_func: _C, **flags: Any) -> _C: ...
|
||||||
|
@overload
|
||||||
|
def filter(self, name: Optional[str] = ..., filter_func: None = ..., **flags: Any) -> Callable[[_C], _C]: ...
|
||||||
|
@overload
|
||||||
|
def simple_tag(self, func: _C) -> _C: ...
|
||||||
|
@overload
|
||||||
def simple_tag(
|
def simple_tag(
|
||||||
self, func: Optional[Union[Callable, str]] = ..., takes_context: Optional[bool] = ..., name: Optional[str] = ...
|
self, takes_context: Optional[bool] = ..., name: Optional[str] = ...
|
||||||
) -> Callable: ...
|
) -> Callable[[_C], _C]: ...
|
||||||
def inclusion_tag(
|
def inclusion_tag(
|
||||||
self,
|
self,
|
||||||
filename: Union[Template, str],
|
filename: Union[Template, str],
|
||||||
func: None = ...,
|
func: None = ...,
|
||||||
takes_context: Optional[bool] = ...,
|
takes_context: Optional[bool] = ...,
|
||||||
name: Optional[str] = ...,
|
name: Optional[str] = ...,
|
||||||
) -> Callable: ...
|
) -> Callable[[_C], _C]: ...
|
||||||
|
|
||||||
class TagHelperNode(Node):
|
class TagHelperNode(Node):
|
||||||
func: Any = ...
|
func: Any = ...
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ class ParallelTestSuite(TestSuite):
|
|||||||
processes: Any = ...
|
processes: Any = ...
|
||||||
failfast: Any = ...
|
failfast: Any = ...
|
||||||
def __init__(self, suite: Any, processes: Any, failfast: bool = ...) -> None: ...
|
def __init__(self, suite: Any, processes: Any, failfast: bool = ...) -> None: ...
|
||||||
def run(self, result: Any): ... # type: ignore
|
def run(self, result: Any): ... # type: ignore[override]
|
||||||
|
|
||||||
class DiscoverRunner:
|
class DiscoverRunner:
|
||||||
test_suite: Any = ...
|
test_suite: Any = ...
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ def get_conditional_response(
|
|||||||
last_modified: Optional[int] = ...,
|
last_modified: Optional[int] = ...,
|
||||||
response: Optional[HttpResponse] = ...,
|
response: Optional[HttpResponse] = ...,
|
||||||
) -> Optional[HttpResponse]: ...
|
) -> Optional[HttpResponse]: ...
|
||||||
def patch_response_headers(response: HttpResponseBase, cache_timeout: float = ...) -> None: ...
|
def patch_response_headers(response: HttpResponseBase, cache_timeout: Optional[int] = ...) -> None: ...
|
||||||
def add_never_cache_headers(response: HttpResponseBase) -> None: ...
|
def add_never_cache_headers(response: HttpResponseBase) -> None: ...
|
||||||
def patch_vary_headers(response: HttpResponseBase, newheaders: Tuple[str]) -> None: ...
|
def patch_vary_headers(response: HttpResponseBase, newheaders: Tuple[str]) -> None: ...
|
||||||
def has_vary_header(response: HttpResponse, header_query: str) -> bool: ...
|
def has_vary_header(response: HttpResponse, header_query: str) -> bool: ...
|
||||||
|
|||||||
@@ -3,7 +3,9 @@ from typing import Callable, Optional, Union
|
|||||||
|
|
||||||
using_sysrandom: bool
|
using_sysrandom: bool
|
||||||
|
|
||||||
def salted_hmac(key_salt: str, value: Union[bytes, str], secret: Optional[Union[bytes, str]] = ...) -> HMAC: ...
|
def salted_hmac(
|
||||||
|
key_salt: str, value: Union[bytes, str], secret: Optional[Union[bytes, str]] = ..., algorithm: str = ...
|
||||||
|
) -> HMAC: ...
|
||||||
def get_random_string(length: int = ..., allowed_chars: str = ...) -> str: ...
|
def get_random_string(length: int = ..., allowed_chars: str = ...) -> str: ...
|
||||||
def constant_time_compare(val1: Union[bytes, str], val2: Union[bytes, str]) -> bool: ...
|
def constant_time_compare(val1: Union[bytes, str], val2: Union[bytes, str]) -> bool: ...
|
||||||
def pbkdf2(
|
def pbkdf2(
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ from typing import Any, Callable, Iterable, Optional, Type, Union, TypeVar
|
|||||||
from django.utils.deprecation import MiddlewareMixin
|
from django.utils.deprecation import MiddlewareMixin
|
||||||
from django.views.generic.base import View
|
from django.views.generic.base import View
|
||||||
|
|
||||||
|
from django.utils.functional import classproperty as classproperty
|
||||||
|
|
||||||
_T = TypeVar("_T", bound=Union[View, Callable]) # Any callable
|
_T = TypeVar("_T", bound=Union[View, Callable]) # Any callable
|
||||||
|
|
||||||
class classonlymethod(classmethod): ...
|
class classonlymethod(classmethod): ...
|
||||||
@@ -12,9 +14,3 @@ def decorator_from_middleware_with_args(middleware_class: type) -> Callable: ...
|
|||||||
def decorator_from_middleware(middleware_class: type) -> Callable: ...
|
def decorator_from_middleware(middleware_class: type) -> Callable: ...
|
||||||
def available_attrs(fn: Callable): ...
|
def available_attrs(fn: Callable): ...
|
||||||
def make_middleware_decorator(middleware_class: Type[MiddlewareMixin]) -> Callable: ...
|
def make_middleware_decorator(middleware_class: Type[MiddlewareMixin]) -> Callable: ...
|
||||||
|
|
||||||
class classproperty:
|
|
||||||
fget: Optional[Callable] = ...
|
|
||||||
def __init__(self, method: Optional[Callable] = ...) -> None: ...
|
|
||||||
def __get__(self, instance: Any, cls: Optional[type] = ...) -> Any: ...
|
|
||||||
def getter(self, method: Callable) -> classproperty: ...
|
|
||||||
|
|||||||
@@ -61,3 +61,9 @@ _PartitionMember = TypeVar("_PartitionMember")
|
|||||||
def partition(
|
def partition(
|
||||||
predicate: Callable, values: List[_PartitionMember]
|
predicate: Callable, values: List[_PartitionMember]
|
||||||
) -> Tuple[List[_PartitionMember], List[_PartitionMember]]: ...
|
) -> Tuple[List[_PartitionMember], List[_PartitionMember]]: ...
|
||||||
|
|
||||||
|
class classproperty:
|
||||||
|
fget: Optional[Callable] = ...
|
||||||
|
def __init__(self, method: Optional[Callable] = ...) -> None: ...
|
||||||
|
def __get__(self, instance: Any, cls: Optional[type] = ...) -> Any: ...
|
||||||
|
def getter(self, method: Callable) -> classproperty: ...
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import functools
|
import functools
|
||||||
from contextlib import ContextDecorator
|
from contextlib import ContextDecorator
|
||||||
from typing import Any, Optional, Callable
|
from typing import Any, Optional, Callable, Union
|
||||||
|
|
||||||
from django.core.handlers.wsgi import WSGIRequest
|
from django.core.handlers.wsgi import WSGIRequest
|
||||||
|
|
||||||
@@ -42,10 +42,12 @@ def npgettext(context: str, singular: str, plural: str, number: int) -> str: ...
|
|||||||
gettext_lazy = gettext
|
gettext_lazy = gettext
|
||||||
ugettext_lazy = ugettext
|
ugettext_lazy = ugettext
|
||||||
pgettext_lazy = pgettext
|
pgettext_lazy = pgettext
|
||||||
ngettext_lazy = ngettext
|
|
||||||
ungettext_lazy = ungettext
|
|
||||||
npgettext_lazy = npgettext
|
|
||||||
|
|
||||||
|
def ngettext_lazy(singular: str, plural: str, number: Union[int, str, None]) -> str: ...
|
||||||
|
|
||||||
|
ungettext_lazy = ngettext_lazy
|
||||||
|
|
||||||
|
def npgettext_lazy(context: str, singular: str, plural: str, number: Union[int, str, None]) -> str: ...
|
||||||
def activate(language: str) -> None: ...
|
def activate(language: str) -> None: ...
|
||||||
def deactivate() -> None: ...
|
def deactivate() -> None: ...
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from typing import Any, Callable, Dict, Optional, Sequence, Type, Union
|
from typing import Any, Callable, Dict, Generic, Optional, Sequence, Type, TypeVar, Union
|
||||||
|
|
||||||
from django.forms.forms import BaseForm
|
from django.forms.forms import BaseForm
|
||||||
from django.forms.models import BaseModelForm
|
from django.forms.models import BaseModelForm
|
||||||
@@ -8,6 +8,8 @@ from typing_extensions import Literal
|
|||||||
|
|
||||||
from django.http import HttpRequest, HttpResponse
|
from django.http import HttpRequest, HttpResponse
|
||||||
|
|
||||||
|
_FormT = TypeVar("_FormT", bound=BaseForm)
|
||||||
|
|
||||||
class AbstractFormMixin(ContextMixin):
|
class AbstractFormMixin(ContextMixin):
|
||||||
initial: Dict[str, Any] = ...
|
initial: Dict[str, Any] = ...
|
||||||
form_class: Optional[Type[BaseForm]] = ...
|
form_class: Optional[Type[BaseForm]] = ...
|
||||||
@@ -18,11 +20,11 @@ class AbstractFormMixin(ContextMixin):
|
|||||||
def get_form_kwargs(self) -> Dict[str, Any]: ...
|
def get_form_kwargs(self) -> Dict[str, Any]: ...
|
||||||
def get_success_url(self) -> str: ...
|
def get_success_url(self) -> str: ...
|
||||||
|
|
||||||
class FormMixin(AbstractFormMixin):
|
class FormMixin(Generic[_FormT], AbstractFormMixin):
|
||||||
def get_form_class(self) -> Type[BaseForm]: ...
|
def get_form_class(self) -> Type[_FormT]: ...
|
||||||
def get_form(self, form_class: Optional[Type[BaseForm]] = ...) -> BaseForm: ...
|
def get_form(self, form_class: Optional[Type[_FormT]] = ...) -> BaseForm: ...
|
||||||
def form_valid(self, form: BaseForm) -> HttpResponse: ...
|
def form_valid(self, form: _FormT) -> HttpResponse: ...
|
||||||
def form_invalid(self, form: BaseForm) -> HttpResponse: ...
|
def form_invalid(self, form: _FormT) -> HttpResponse: ...
|
||||||
|
|
||||||
class ModelFormMixin(AbstractFormMixin, SingleObjectMixin):
|
class ModelFormMixin(AbstractFormMixin, SingleObjectMixin):
|
||||||
fields: Optional[Union[Sequence[str], Literal["__all__"]]] = ...
|
fields: Optional[Union[Sequence[str], Literal["__all__"]]] = ...
|
||||||
@@ -36,8 +38,8 @@ class ProcessFormView(View):
|
|||||||
def post(self, request: HttpRequest, *args: str, **kwargs: Any) -> HttpResponse: ...
|
def post(self, request: HttpRequest, *args: str, **kwargs: Any) -> HttpResponse: ...
|
||||||
def put(self, *args: str, **kwargs: Any) -> HttpResponse: ...
|
def put(self, *args: str, **kwargs: Any) -> HttpResponse: ...
|
||||||
|
|
||||||
class BaseFormView(FormMixin, ProcessFormView): ...
|
class BaseFormView(FormMixin[_FormT], ProcessFormView): ...
|
||||||
class FormView(TemplateResponseMixin, BaseFormView): ...
|
class FormView(TemplateResponseMixin, BaseFormView[_FormT]): ...
|
||||||
class BaseCreateView(ModelFormMixin, ProcessFormView): ...
|
class BaseCreateView(ModelFormMixin, ProcessFormView): ...
|
||||||
class CreateView(SingleObjectTemplateResponseMixin, BaseCreateView): ...
|
class CreateView(SingleObjectTemplateResponseMixin, BaseCreateView): ...
|
||||||
class BaseUpdateView(ModelFormMixin, ProcessFormView): ...
|
class BaseUpdateView(ModelFormMixin, ProcessFormView): ...
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ from typing import Any, Optional
|
|||||||
from django.http.request import HttpRequest
|
from django.http.request import HttpRequest
|
||||||
from django.http.response import FileResponse
|
from django.http.response import FileResponse
|
||||||
|
|
||||||
def serve(request: HttpRequest, path: str, document_root: str = ..., show_indexes: bool = ...) -> FileResponse: ...
|
def serve(request: HttpRequest, path: str, document_root: Optional[str] = ..., show_indexes: bool = ...) -> FileResponse: ...
|
||||||
|
|
||||||
DEFAULT_DIRECTORY_INDEX_TEMPLATE: str
|
DEFAULT_DIRECTORY_INDEX_TEMPLATE: str
|
||||||
template_translatable: Any
|
template_translatable: Any
|
||||||
|
|||||||
49
django_stubs_ext/README.md
Normal file
49
django_stubs_ext/README.md
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
# Extensions and monkey-patching for django-stubs
|
||||||
|
|
||||||
|
[](https://travis-ci.com/typeddjango/django-stubs)
|
||||||
|
[](http://mypy-lang.org/)
|
||||||
|
[](https://gitter.im/mypy-django/Lobby)
|
||||||
|
|
||||||
|
|
||||||
|
This package contains extensions and monkey-patching functions for the [django-stubs](https://github.com/typeddjango/django-stubs) package. Certain features of django-stubs (i.e. generic django classes that don't define the `__class_getitem__` method) require runtime monkey-patching, which can't be done with type stubs. These extensions were split into a separate package so library consumers don't need `mypy` as a runtime dependency ([#526](https://github.com/typeddjango/django-stubs/pull/526#pullrequestreview-525798031)).
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install django-stubs-ext
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
In your Django application, use the following code:
|
||||||
|
|
||||||
|
```py
|
||||||
|
import django_stubs_ext
|
||||||
|
|
||||||
|
django_stubs_ext.monkeypatch()
|
||||||
|
```
|
||||||
|
|
||||||
|
This only needs to be called once, so the call to `monkeypatch` should be placed in your top-level settings.
|
||||||
|
|
||||||
|
## Version compatibility
|
||||||
|
|
||||||
|
Since django-stubs supports multiple Django versions, this package takes care to only monkey-patch the features needed by your django version, and decides which features to patch at runtime. This is completely safe, as (currently) we only add a `__class_getitem__` method that does nothing:
|
||||||
|
|
||||||
|
```py
|
||||||
|
@classmethod
|
||||||
|
def __class_getitem__(cls, *args, **kwargs):
|
||||||
|
return cls
|
||||||
|
```
|
||||||
|
|
||||||
|
## To get help
|
||||||
|
|
||||||
|
For help with django-stubs, please view the main repository at <https://github.com/typeddjango/django-stubs>
|
||||||
|
|
||||||
|
We have a Gitter chat here: <https://gitter.im/mypy-django/Lobby>
|
||||||
|
If you think you have a more generic typing issue, please refer to <https://github.com/python/mypy> and their Gitter.
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
The django-stubs-ext package is part of the [django-stubs](https://github.com/typeddjango/django-stubs) monorepo. If you would like to contribute, please view the django-stubs [contribution guide](https://github.com/typeddjango/django-stubs/blob/master/CONTRIBUTING.md).
|
||||||
|
|
||||||
|
You can always also reach out in gitter to discuss your contributions!
|
||||||
3
django_stubs_ext/django_stubs_ext/__init__.py
Normal file
3
django_stubs_ext/django_stubs_ext/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
from .patch import monkeypatch as monkeypatch
|
||||||
|
|
||||||
|
__all__ = ["monkeypatch"]
|
||||||
60
django_stubs_ext/django_stubs_ext/patch.py
Normal file
60
django_stubs_ext/django_stubs_ext/patch.py
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
from typing import Any, Generic, List, Optional, Tuple, Type, TypeVar
|
||||||
|
|
||||||
|
from django import VERSION as VERSION
|
||||||
|
from django.contrib.admin import ModelAdmin
|
||||||
|
from django.contrib.admin.options import BaseModelAdmin
|
||||||
|
from django.db.models.fields import Field
|
||||||
|
from django.db.models.manager import BaseManager
|
||||||
|
from django.db.models.query import QuerySet
|
||||||
|
from django.views.generic.edit import FormMixin
|
||||||
|
|
||||||
|
_T = TypeVar("_T")
|
||||||
|
_VersionSpec = Tuple[int, int]
|
||||||
|
|
||||||
|
|
||||||
|
class MPGeneric(Generic[_T]):
|
||||||
|
"""Create a data class to hold metadata about the gneric classes needing monkeypatching.
|
||||||
|
|
||||||
|
The `version` param is optional, and a value of `None` means that the monkeypatch is
|
||||||
|
version-independent.
|
||||||
|
|
||||||
|
This is slightly overkill for our purposes, but useful for future-proofing against any
|
||||||
|
possible issues we may run into with this method.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, cls: Type[_T], version: Optional[_VersionSpec] = None):
|
||||||
|
"""Set the data fields, basic constructor."""
|
||||||
|
self.version = version
|
||||||
|
self.cls = cls
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
"""Better representation in tests and debug."""
|
||||||
|
return "<MPGeneric: {}, versions={}>".format(self.cls, self.version or "all")
|
||||||
|
|
||||||
|
|
||||||
|
# certain django classes need to be generic, but lack the __class_getitem__ dunder needed to
|
||||||
|
# annotate them: https://github.com/typeddjango/django-stubs/issues/507
|
||||||
|
# this list stores them so `monkeypatch` can fix them when called
|
||||||
|
_need_generic: List[MPGeneric[Any]] = [
|
||||||
|
MPGeneric(ModelAdmin),
|
||||||
|
MPGeneric(FormMixin),
|
||||||
|
MPGeneric(BaseModelAdmin),
|
||||||
|
MPGeneric(Field),
|
||||||
|
# These types do have native `__class_getitem__` method since django 3.1:
|
||||||
|
MPGeneric(QuerySet, (3, 1)),
|
||||||
|
MPGeneric(BaseManager, (3, 1)),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
# currently just adds the __class_getitem__ dunder. if more monkeypatching is needed, add it here
|
||||||
|
def monkeypatch() -> None:
|
||||||
|
"""Monkey patch django as necessary to work properly with mypy."""
|
||||||
|
suited_for_this_version = filter(
|
||||||
|
lambda spec: spec.version is None or VERSION[:2] <= spec.version,
|
||||||
|
_need_generic,
|
||||||
|
)
|
||||||
|
for el in suited_for_this_version:
|
||||||
|
el.cls.__class_getitem__ = classmethod(lambda cls, *args, **kwargs: cls)
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ["monkeypatch"]
|
||||||
17
django_stubs_ext/release.sh
Normal file
17
django_stubs_ext/release.sh
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -ex
|
||||||
|
|
||||||
|
if [[ -z $(git status -s) ]]
|
||||||
|
then
|
||||||
|
if [[ "$VIRTUAL_ENV" != "" ]]
|
||||||
|
then
|
||||||
|
pip install --upgrade setuptools wheel twine
|
||||||
|
python setup.py sdist bdist_wheel
|
||||||
|
twine upload dist/*
|
||||||
|
rm -rf dist/ build/
|
||||||
|
else
|
||||||
|
echo "this script must be executed inside an active virtual env, aborting"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "git working tree is not clean, aborting"
|
||||||
|
fi
|
||||||
2
django_stubs_ext/setup.cfg
Normal file
2
django_stubs_ext/setup.cfg
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
[metadata]
|
||||||
|
license_file = ../LICENSE.txt
|
||||||
42
django_stubs_ext/setup.py
Normal file
42
django_stubs_ext/setup.py
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
from distutils.core import setup
|
||||||
|
|
||||||
|
from setuptools import find_packages
|
||||||
|
|
||||||
|
with open("README.md") as f:
|
||||||
|
readme = f.read()
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
"django",
|
||||||
|
]
|
||||||
|
|
||||||
|
setup(
|
||||||
|
name="django-stubs-ext",
|
||||||
|
version="0.1.0",
|
||||||
|
description="Monkey-patching and extensions for django-stubs",
|
||||||
|
long_description=readme,
|
||||||
|
long_description_content_type="text/markdown",
|
||||||
|
license="MIT",
|
||||||
|
url="https://github.com/typeddjango/django-stubs",
|
||||||
|
author="Simula Proxy",
|
||||||
|
author_email="3nki.nam.shub@gmail.com",
|
||||||
|
py_modules=[],
|
||||||
|
python_requires=">=3.6",
|
||||||
|
install_requires=dependencies,
|
||||||
|
packages=["django_stubs_ext", *find_packages(exclude=["scripts"])],
|
||||||
|
classifiers=[
|
||||||
|
"License :: OSI Approved :: MIT License",
|
||||||
|
"Operating System :: OS Independent",
|
||||||
|
"Programming Language :: Python :: 3.6",
|
||||||
|
"Programming Language :: Python :: 3.7",
|
||||||
|
"Programming Language :: Python :: 3.8",
|
||||||
|
"Programming Language :: Python :: 3.9",
|
||||||
|
"Typing :: Typed",
|
||||||
|
"Framework :: Django",
|
||||||
|
"Framework :: Django :: 2.2",
|
||||||
|
"Framework :: Django :: 3.0",
|
||||||
|
"Framework :: Django :: 3.1",
|
||||||
|
],
|
||||||
|
project_urls={
|
||||||
|
"Release notes": "https://github.com/typeddjango/django-stubs/releases",
|
||||||
|
},
|
||||||
|
)
|
||||||
67
django_stubs_ext/tests/test_monkeypatching.py
Normal file
67
django_stubs_ext/tests/test_monkeypatching.py
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
from contextlib import suppress
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from _pytest.fixtures import FixtureRequest
|
||||||
|
from _pytest.monkeypatch import MonkeyPatch
|
||||||
|
from typing_extensions import Protocol
|
||||||
|
|
||||||
|
import django_stubs_ext
|
||||||
|
from django_stubs_ext import patch
|
||||||
|
from django_stubs_ext.patch import _need_generic, _VersionSpec
|
||||||
|
|
||||||
|
|
||||||
|
class _MakeGenericClasses(Protocol):
|
||||||
|
"""Used to represent a type of ``make_generic_classes`` fixture."""
|
||||||
|
|
||||||
|
def __call__(self, django_version: Optional[_VersionSpec] = None) -> None:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="function")
|
||||||
|
def make_generic_classes(
|
||||||
|
request: FixtureRequest,
|
||||||
|
monkeypatch: MonkeyPatch,
|
||||||
|
) -> _MakeGenericClasses:
|
||||||
|
def fin() -> None:
|
||||||
|
for el in _need_generic:
|
||||||
|
with suppress(AttributeError):
|
||||||
|
delattr(el.cls, "__class_getitem__")
|
||||||
|
|
||||||
|
def factory(django_version: Optional[_VersionSpec] = None) -> None:
|
||||||
|
if django_version is not None:
|
||||||
|
monkeypatch.setattr(patch, "VERSION", django_version)
|
||||||
|
django_stubs_ext.monkeypatch()
|
||||||
|
|
||||||
|
request.addfinalizer(fin)
|
||||||
|
return factory
|
||||||
|
|
||||||
|
|
||||||
|
def test_patched_generics(make_generic_classes: _MakeGenericClasses) -> None:
|
||||||
|
"""Test that the generics actually get patched."""
|
||||||
|
make_generic_classes()
|
||||||
|
|
||||||
|
for el in _need_generic:
|
||||||
|
if el.version is None:
|
||||||
|
assert el.cls[type] is el.cls # `type` is arbitrary
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"django_version",
|
||||||
|
[
|
||||||
|
(2, 2),
|
||||||
|
(3, 0),
|
||||||
|
(3, 1),
|
||||||
|
(3, 2),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_patched_version_specific(
|
||||||
|
django_version: _VersionSpec,
|
||||||
|
make_generic_classes: _MakeGenericClasses,
|
||||||
|
) -> None:
|
||||||
|
"""Test version speicific types."""
|
||||||
|
make_generic_classes(django_version)
|
||||||
|
|
||||||
|
for el in _need_generic:
|
||||||
|
if el.version is not None and django_version <= el.version:
|
||||||
|
assert el.cls[int] is el.cls
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
[flake8]
|
|
||||||
filename =
|
|
||||||
*.pyi
|
|
||||||
exclude =
|
|
||||||
django-sources
|
|
||||||
test-data
|
|
||||||
mypy_django_plugin
|
|
||||||
scripts
|
|
||||||
select = F401, Y
|
|
||||||
max_line_length = 120
|
|
||||||
per-file-ignores =
|
|
||||||
*__init__.pyi: F401
|
|
||||||
base_user.pyi: Y003
|
|
||||||
models.pyi: Y003
|
|
||||||
14
mypy.ini
14
mypy.ini
@@ -1,2 +1,14 @@
|
|||||||
[mypy]
|
[mypy]
|
||||||
warn_unused_ignores = True
|
strict_optional = True
|
||||||
|
ignore_missing_imports = True
|
||||||
|
check_untyped_defs = True
|
||||||
|
warn_no_return = False
|
||||||
|
show_traceback = True
|
||||||
|
allow_redefinition = True
|
||||||
|
incremental = True
|
||||||
|
|
||||||
|
plugins =
|
||||||
|
mypy_django_plugin.main
|
||||||
|
|
||||||
|
[mypy.plugins.django-stubs]
|
||||||
|
django_settings_module = scripts.django_tests_settings
|
||||||
|
|||||||
@@ -2,9 +2,7 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
from typing import (
|
from typing import TYPE_CHECKING, Dict, Iterable, Iterator, Optional, Set, Tuple, Type, Union
|
||||||
TYPE_CHECKING, Dict, Iterable, Iterator, Optional, Set, Tuple, Type, Union,
|
|
||||||
)
|
|
||||||
|
|
||||||
from django.core.exceptions import FieldError
|
from django.core.exceptions import FieldError
|
||||||
from django.db import models
|
from django.db import models
|
||||||
@@ -26,9 +24,11 @@ from mypy_django_plugin.lib import fullnames, helpers
|
|||||||
try:
|
try:
|
||||||
from django.contrib.postgres.fields import ArrayField
|
from django.contrib.postgres.fields import ArrayField
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
|
||||||
class ArrayField: # type: ignore
|
class ArrayField: # type: ignore
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from django.apps.registry import Apps # noqa: F401
|
from django.apps.registry import Apps # noqa: F401
|
||||||
from django.conf import LazySettings # noqa: F401
|
from django.conf import LazySettings # noqa: F401
|
||||||
@@ -45,9 +45,9 @@ def temp_environ():
|
|||||||
os.environ.update(environ)
|
os.environ.update(environ)
|
||||||
|
|
||||||
|
|
||||||
def initialize_django(settings_module: str) -> Tuple['Apps', 'LazySettings']:
|
def initialize_django(settings_module: str) -> Tuple["Apps", "LazySettings"]:
|
||||||
with temp_environ():
|
with temp_environ():
|
||||||
os.environ['DJANGO_SETTINGS_MODULE'] = settings_module
|
os.environ["DJANGO_SETTINGS_MODULE"] = settings_module
|
||||||
|
|
||||||
# add current directory to sys.path
|
# add current directory to sys.path
|
||||||
sys.path.append(os.getcwd())
|
sys.path.append(os.getcwd())
|
||||||
@@ -60,8 +60,8 @@ def initialize_django(settings_module: str) -> Tuple['Apps', 'LazySettings']:
|
|||||||
models.QuerySet.__class_getitem__ = classmethod(noop_class_getitem) # type: ignore
|
models.QuerySet.__class_getitem__ = classmethod(noop_class_getitem) # type: ignore
|
||||||
models.Manager.__class_getitem__ = classmethod(noop_class_getitem) # type: ignore
|
models.Manager.__class_getitem__ = classmethod(noop_class_getitem) # type: ignore
|
||||||
|
|
||||||
from django.conf import settings
|
|
||||||
from django.apps import apps
|
from django.apps import apps
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
apps.get_models.cache_clear() # type: ignore
|
apps.get_models.cache_clear() # type: ignore
|
||||||
apps.get_swappable_settings_name.cache_clear() # type: ignore
|
apps.get_swappable_settings_name.cache_clear() # type: ignore
|
||||||
@@ -100,15 +100,13 @@ class DjangoContext:
|
|||||||
modules[concrete_model_cls.__module__].add(concrete_model_cls)
|
modules[concrete_model_cls.__module__].add(concrete_model_cls)
|
||||||
# collect abstract=True models
|
# collect abstract=True models
|
||||||
for model_cls in concrete_model_cls.mro()[1:]:
|
for model_cls in concrete_model_cls.mro()[1:]:
|
||||||
if (issubclass(model_cls, Model)
|
if issubclass(model_cls, Model) and hasattr(model_cls, "_meta") and model_cls._meta.abstract:
|
||||||
and hasattr(model_cls, '_meta')
|
|
||||||
and model_cls._meta.abstract):
|
|
||||||
modules[model_cls.__module__].add(model_cls)
|
modules[model_cls.__module__].add(model_cls)
|
||||||
return modules
|
return modules
|
||||||
|
|
||||||
def get_model_class_by_fullname(self, fullname: str) -> Optional[Type[Model]]:
|
def get_model_class_by_fullname(self, fullname: str) -> Optional[Type[Model]]:
|
||||||
# Returns None if Model is abstract
|
# Returns None if Model is abstract
|
||||||
module, _, model_cls_name = fullname.rpartition('.')
|
module, _, model_cls_name = fullname.rpartition(".")
|
||||||
for model_cls in self.model_modules.get(module, set()):
|
for model_cls in self.model_modules.get(module, set()):
|
||||||
if model_cls.__name__ == model_cls_name:
|
if model_cls.__name__ == model_cls_name:
|
||||||
return model_cls
|
return model_cls
|
||||||
@@ -128,7 +126,7 @@ class DjangoContext:
|
|||||||
if isinstance(field, (RelatedField, ForeignObjectRel)):
|
if isinstance(field, (RelatedField, ForeignObjectRel)):
|
||||||
related_model_cls = field.related_model
|
related_model_cls = field.related_model
|
||||||
primary_key_field = self.get_primary_key_field(related_model_cls)
|
primary_key_field = self.get_primary_key_field(related_model_cls)
|
||||||
primary_key_type = self.get_field_get_type(api, primary_key_field, method='init')
|
primary_key_type = self.get_field_get_type(api, primary_key_field, method="init")
|
||||||
|
|
||||||
rel_model_info = helpers.lookup_class_typeinfo(api, related_model_cls)
|
rel_model_info = helpers.lookup_class_typeinfo(api, related_model_cls)
|
||||||
if rel_model_info is None:
|
if rel_model_info is None:
|
||||||
@@ -140,17 +138,18 @@ class DjangoContext:
|
|||||||
field_info = helpers.lookup_class_typeinfo(api, field.__class__)
|
field_info = helpers.lookup_class_typeinfo(api, field.__class__)
|
||||||
if field_info is None:
|
if field_info is None:
|
||||||
return AnyType(TypeOfAny.explicit)
|
return AnyType(TypeOfAny.explicit)
|
||||||
return helpers.get_private_descriptor_type(field_info, '_pyi_lookup_exact_type',
|
return helpers.get_private_descriptor_type(field_info, "_pyi_lookup_exact_type", is_nullable=field.null)
|
||||||
is_nullable=field.null)
|
|
||||||
|
|
||||||
def get_primary_key_field(self, model_cls: Type[Model]) -> Field:
|
def get_primary_key_field(self, model_cls: Type[Model]) -> Field:
|
||||||
for field in model_cls._meta.get_fields():
|
for field in model_cls._meta.get_fields():
|
||||||
if isinstance(field, Field):
|
if isinstance(field, Field):
|
||||||
if field.primary_key:
|
if field.primary_key:
|
||||||
return field
|
return field
|
||||||
raise ValueError('No primary key defined')
|
raise ValueError("No primary key defined")
|
||||||
|
|
||||||
def get_expected_types(self, api: TypeChecker, model_cls: Type[Model], *, method: str) -> Dict[str, MypyType]:
|
def get_expected_types(self, api: TypeChecker, model_cls: Type[Model], *, method: str) -> Dict[str, MypyType]:
|
||||||
|
contenttypes_in_apps = self.apps_registry.is_installed("django.contrib.contenttypes")
|
||||||
|
if contenttypes_in_apps:
|
||||||
from django.contrib.contenttypes.fields import GenericForeignKey
|
from django.contrib.contenttypes.fields import GenericForeignKey
|
||||||
|
|
||||||
expected_types = {}
|
expected_types = {}
|
||||||
@@ -158,7 +157,7 @@ class DjangoContext:
|
|||||||
if not model_cls._meta.abstract:
|
if not model_cls._meta.abstract:
|
||||||
primary_key_field = self.get_primary_key_field(model_cls)
|
primary_key_field = self.get_primary_key_field(model_cls)
|
||||||
field_set_type = self.get_field_set_type(api, primary_key_field, method=method)
|
field_set_type = self.get_field_set_type(api, primary_key_field, method=method)
|
||||||
expected_types['pk'] = field_set_type
|
expected_types["pk"] = field_set_type
|
||||||
|
|
||||||
for field in model_cls._meta.get_fields():
|
for field in model_cls._meta.get_fields():
|
||||||
if isinstance(field, Field):
|
if isinstance(field, Field):
|
||||||
@@ -188,20 +187,18 @@ class DjangoContext:
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
is_nullable = self.get_field_nullability(field, method)
|
is_nullable = self.get_field_nullability(field, method)
|
||||||
foreign_key_set_type = helpers.get_private_descriptor_type(foreign_key_info,
|
foreign_key_set_type = helpers.get_private_descriptor_type(
|
||||||
'_pyi_private_set_type',
|
foreign_key_info, "_pyi_private_set_type", is_nullable=is_nullable
|
||||||
is_nullable=is_nullable)
|
)
|
||||||
model_set_type = helpers.convert_any_to_type(foreign_key_set_type,
|
model_set_type = helpers.convert_any_to_type(foreign_key_set_type, Instance(related_model_info, []))
|
||||||
Instance(related_model_info, []))
|
|
||||||
|
|
||||||
expected_types[field_name] = model_set_type
|
expected_types[field_name] = model_set_type
|
||||||
|
|
||||||
elif isinstance(field, GenericForeignKey):
|
elif contenttypes_in_apps and isinstance(field, GenericForeignKey):
|
||||||
# it's generic, so cannot set specific model
|
# it's generic, so cannot set specific model
|
||||||
field_name = field.name
|
field_name = field.name
|
||||||
gfk_info = helpers.lookup_class_typeinfo(api, field.__class__)
|
gfk_info = helpers.lookup_class_typeinfo(api, field.__class__)
|
||||||
gfk_set_type = helpers.get_private_descriptor_type(gfk_info, '_pyi_private_set_type',
|
gfk_set_type = helpers.get_private_descriptor_type(gfk_info, "_pyi_private_set_type", is_nullable=True)
|
||||||
is_nullable=True)
|
|
||||||
expected_types[field_name] = gfk_set_type
|
expected_types[field_name] = gfk_set_type
|
||||||
|
|
||||||
return expected_types
|
return expected_types
|
||||||
@@ -230,11 +227,10 @@ class DjangoContext:
|
|||||||
nullable = field.null
|
nullable = field.null
|
||||||
if not nullable and isinstance(field, CharField) and field.blank:
|
if not nullable and isinstance(field, CharField) and field.blank:
|
||||||
return True
|
return True
|
||||||
if method == '__init__':
|
if method == "__init__":
|
||||||
if ((isinstance(field, Field) and field.primary_key)
|
if (isinstance(field, Field) and field.primary_key) or isinstance(field, ForeignKey):
|
||||||
or isinstance(field, ForeignKey)):
|
|
||||||
return True
|
return True
|
||||||
if method == 'create':
|
if method == "create":
|
||||||
if isinstance(field, AutoField):
|
if isinstance(field, AutoField):
|
||||||
return True
|
return True
|
||||||
if isinstance(field, Field) and field.has_default():
|
if isinstance(field, Field) and field.has_default():
|
||||||
@@ -251,8 +247,9 @@ class DjangoContext:
|
|||||||
if field_info is None:
|
if field_info is None:
|
||||||
return AnyType(TypeOfAny.from_error)
|
return AnyType(TypeOfAny.from_error)
|
||||||
|
|
||||||
field_set_type = helpers.get_private_descriptor_type(field_info, '_pyi_private_set_type',
|
field_set_type = helpers.get_private_descriptor_type(
|
||||||
is_nullable=self.get_field_nullability(field, method))
|
field_info, "_pyi_private_set_type", is_nullable=self.get_field_nullability(field, method)
|
||||||
|
)
|
||||||
if isinstance(target_field, ArrayField):
|
if isinstance(target_field, ArrayField):
|
||||||
argument_field_type = self.get_field_set_type(api, target_field.base_field, method=method)
|
argument_field_type = self.get_field_set_type(api, target_field.base_field, method=method)
|
||||||
field_set_type = helpers.convert_any_to_type(field_set_type, argument_field_type)
|
field_set_type = helpers.convert_any_to_type(field_set_type, argument_field_type)
|
||||||
@@ -270,7 +267,7 @@ class DjangoContext:
|
|||||||
if related_model_cls is None:
|
if related_model_cls is None:
|
||||||
return AnyType(TypeOfAny.from_error)
|
return AnyType(TypeOfAny.from_error)
|
||||||
|
|
||||||
if method == 'values':
|
if method == "values":
|
||||||
primary_key_field = self.get_primary_key_field(related_model_cls)
|
primary_key_field = self.get_primary_key_field(related_model_cls)
|
||||||
return self.get_field_get_type(api, primary_key_field, method=method)
|
return self.get_field_get_type(api, primary_key_field, method=method)
|
||||||
|
|
||||||
@@ -280,8 +277,7 @@ class DjangoContext:
|
|||||||
|
|
||||||
return Instance(model_info, [])
|
return Instance(model_info, [])
|
||||||
else:
|
else:
|
||||||
return helpers.get_private_descriptor_type(field_info, '_pyi_private_get_type',
|
return helpers.get_private_descriptor_type(field_info, "_pyi_private_get_type", is_nullable=is_nullable)
|
||||||
is_nullable=is_nullable)
|
|
||||||
|
|
||||||
def get_field_related_model_cls(self, field: Union[RelatedField, ForeignObjectRel]) -> Optional[Type[Model]]:
|
def get_field_related_model_cls(self, field: Union[RelatedField, ForeignObjectRel]) -> Optional[Type[Model]]:
|
||||||
if isinstance(field, RelatedField):
|
if isinstance(field, RelatedField):
|
||||||
@@ -290,26 +286,25 @@ class DjangoContext:
|
|||||||
related_model_cls = field.field.model
|
related_model_cls = field.field.model
|
||||||
|
|
||||||
if isinstance(related_model_cls, str):
|
if isinstance(related_model_cls, str):
|
||||||
if related_model_cls == 'self':
|
if related_model_cls == "self":
|
||||||
# same model
|
# same model
|
||||||
related_model_cls = field.model
|
related_model_cls = field.model
|
||||||
elif '.' not in related_model_cls:
|
elif "." not in related_model_cls:
|
||||||
# same file model
|
# same file model
|
||||||
related_model_fullname = field.model.__module__ + '.' + related_model_cls
|
related_model_fullname = field.model.__module__ + "." + related_model_cls
|
||||||
related_model_cls = self.get_model_class_by_fullname(related_model_fullname)
|
related_model_cls = self.get_model_class_by_fullname(related_model_fullname)
|
||||||
else:
|
else:
|
||||||
related_model_cls = self.apps_registry.get_model(related_model_cls)
|
related_model_cls = self.apps_registry.get_model(related_model_cls)
|
||||||
|
|
||||||
return related_model_cls
|
return related_model_cls
|
||||||
|
|
||||||
def _resolve_field_from_parts(self,
|
def _resolve_field_from_parts(
|
||||||
field_parts: Iterable[str],
|
self, field_parts: Iterable[str], model_cls: Type[Model]
|
||||||
model_cls: Type[Model]
|
|
||||||
) -> Union[Field, ForeignObjectRel]:
|
) -> Union[Field, ForeignObjectRel]:
|
||||||
currently_observed_model = model_cls
|
currently_observed_model = model_cls
|
||||||
field = None
|
field = None
|
||||||
for field_part in field_parts:
|
for field_part in field_parts:
|
||||||
if field_part == 'pk':
|
if field_part == "pk":
|
||||||
field = self.get_primary_key_field(currently_observed_model)
|
field = self.get_primary_key_field(currently_observed_model)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@@ -317,8 +312,7 @@ class DjangoContext:
|
|||||||
if isinstance(field, RelatedField):
|
if isinstance(field, RelatedField):
|
||||||
currently_observed_model = field.related_model
|
currently_observed_model = field.related_model
|
||||||
model_name = currently_observed_model._meta.model_name
|
model_name = currently_observed_model._meta.model_name
|
||||||
if (model_name is not None
|
if model_name is not None and field_part == (model_name + "_id"):
|
||||||
and field_part == (model_name + '_id')):
|
|
||||||
field = self.get_primary_key_field(currently_observed_model)
|
field = self.get_primary_key_field(currently_observed_model)
|
||||||
|
|
||||||
if isinstance(field, ForeignObjectRel):
|
if isinstance(field, ForeignObjectRel):
|
||||||
@@ -368,13 +362,13 @@ class DjangoContext:
|
|||||||
if lookup_base.args and isinstance(lookup_base.args[0], Instance):
|
if lookup_base.args and isinstance(lookup_base.args[0], Instance):
|
||||||
lookup_type: MypyType = lookup_base.args[0]
|
lookup_type: MypyType = lookup_base.args[0]
|
||||||
# if it's Field, consider lookup_type a __get__ of current field
|
# if it's Field, consider lookup_type a __get__ of current field
|
||||||
if (isinstance(lookup_type, Instance)
|
if isinstance(lookup_type, Instance) and lookup_type.type.fullname == fullnames.FIELD_FULLNAME:
|
||||||
and lookup_type.type.fullname == fullnames.FIELD_FULLNAME):
|
|
||||||
field_info = helpers.lookup_class_typeinfo(helpers.get_typechecker_api(ctx), field.__class__)
|
field_info = helpers.lookup_class_typeinfo(helpers.get_typechecker_api(ctx), field.__class__)
|
||||||
if field_info is None:
|
if field_info is None:
|
||||||
return AnyType(TypeOfAny.explicit)
|
return AnyType(TypeOfAny.explicit)
|
||||||
lookup_type = helpers.get_private_descriptor_type(field_info, '_pyi_private_get_type',
|
lookup_type = helpers.get_private_descriptor_type(
|
||||||
is_nullable=field.null)
|
field_info, "_pyi_private_get_type", is_nullable=field.null
|
||||||
|
)
|
||||||
return lookup_type
|
return lookup_type
|
||||||
|
|
||||||
return AnyType(TypeOfAny.explicit)
|
return AnyType(TypeOfAny.explicit)
|
||||||
|
|||||||
@@ -1,39 +1,36 @@
|
|||||||
|
ABSTRACT_USER_MODEL_FULLNAME = "django.contrib.auth.models.AbstractUser"
|
||||||
|
PERMISSION_MIXIN_CLASS_FULLNAME = "django.contrib.auth.models.PermissionsMixin"
|
||||||
|
MODEL_CLASS_FULLNAME = "django.db.models.base.Model"
|
||||||
|
FIELD_FULLNAME = "django.db.models.fields.Field"
|
||||||
|
CHAR_FIELD_FULLNAME = "django.db.models.fields.CharField"
|
||||||
|
ARRAY_FIELD_FULLNAME = "django.contrib.postgres.fields.array.ArrayField"
|
||||||
|
AUTO_FIELD_FULLNAME = "django.db.models.fields.AutoField"
|
||||||
|
GENERIC_FOREIGN_KEY_FULLNAME = "django.contrib.contenttypes.fields.GenericForeignKey"
|
||||||
|
FOREIGN_KEY_FULLNAME = "django.db.models.fields.related.ForeignKey"
|
||||||
|
ONETOONE_FIELD_FULLNAME = "django.db.models.fields.related.OneToOneField"
|
||||||
|
MANYTOMANY_FIELD_FULLNAME = "django.db.models.fields.related.ManyToManyField"
|
||||||
|
DUMMY_SETTINGS_BASE_CLASS = "django.conf._DjangoConfLazyObject"
|
||||||
|
|
||||||
MODEL_CLASS_FULLNAME = 'django.db.models.base.Model'
|
QUERYSET_CLASS_FULLNAME = "django.db.models.query.QuerySet"
|
||||||
FIELD_FULLNAME = 'django.db.models.fields.Field'
|
BASE_MANAGER_CLASS_FULLNAME = "django.db.models.manager.BaseManager"
|
||||||
CHAR_FIELD_FULLNAME = 'django.db.models.fields.CharField'
|
MANAGER_CLASS_FULLNAME = "django.db.models.manager.Manager"
|
||||||
ARRAY_FIELD_FULLNAME = 'django.contrib.postgres.fields.array.ArrayField'
|
RELATED_MANAGER_CLASS = "django.db.models.manager.RelatedManager"
|
||||||
AUTO_FIELD_FULLNAME = 'django.db.models.fields.AutoField'
|
|
||||||
GENERIC_FOREIGN_KEY_FULLNAME = 'django.contrib.contenttypes.fields.GenericForeignKey'
|
|
||||||
FOREIGN_KEY_FULLNAME = 'django.db.models.fields.related.ForeignKey'
|
|
||||||
ONETOONE_FIELD_FULLNAME = 'django.db.models.fields.related.OneToOneField'
|
|
||||||
MANYTOMANY_FIELD_FULLNAME = 'django.db.models.fields.related.ManyToManyField'
|
|
||||||
DUMMY_SETTINGS_BASE_CLASS = 'django.conf._DjangoConfLazyObject'
|
|
||||||
|
|
||||||
QUERYSET_CLASS_FULLNAME = 'django.db.models.query.QuerySet'
|
BASEFORM_CLASS_FULLNAME = "django.forms.forms.BaseForm"
|
||||||
BASE_MANAGER_CLASS_FULLNAME = 'django.db.models.manager.BaseManager'
|
FORM_CLASS_FULLNAME = "django.forms.forms.Form"
|
||||||
MANAGER_CLASS_FULLNAME = 'django.db.models.manager.Manager'
|
MODELFORM_CLASS_FULLNAME = "django.forms.models.ModelForm"
|
||||||
RELATED_MANAGER_CLASS = 'django.db.models.manager.RelatedManager'
|
|
||||||
|
|
||||||
BASEFORM_CLASS_FULLNAME = 'django.forms.forms.BaseForm'
|
FORM_MIXIN_CLASS_FULLNAME = "django.views.generic.edit.FormMixin"
|
||||||
FORM_CLASS_FULLNAME = 'django.forms.forms.Form'
|
|
||||||
MODELFORM_CLASS_FULLNAME = 'django.forms.models.ModelForm'
|
|
||||||
|
|
||||||
FORM_MIXIN_CLASS_FULLNAME = 'django.views.generic.edit.FormMixin'
|
|
||||||
|
|
||||||
MANAGER_CLASSES = {
|
MANAGER_CLASSES = {
|
||||||
MANAGER_CLASS_FULLNAME,
|
MANAGER_CLASS_FULLNAME,
|
||||||
BASE_MANAGER_CLASS_FULLNAME,
|
BASE_MANAGER_CLASS_FULLNAME,
|
||||||
}
|
}
|
||||||
|
|
||||||
RELATED_FIELDS_CLASSES = {
|
RELATED_FIELDS_CLASSES = {FOREIGN_KEY_FULLNAME, ONETOONE_FIELD_FULLNAME, MANYTOMANY_FIELD_FULLNAME}
|
||||||
FOREIGN_KEY_FULLNAME,
|
|
||||||
ONETOONE_FIELD_FULLNAME,
|
|
||||||
MANYTOMANY_FIELD_FULLNAME
|
|
||||||
}
|
|
||||||
|
|
||||||
MIGRATION_CLASS_FULLNAME = 'django.db.migrations.migration.Migration'
|
MIGRATION_CLASS_FULLNAME = "django.db.migrations.migration.Migration"
|
||||||
OPTIONS_CLASS_FULLNAME = 'django.db.models.options.Options'
|
OPTIONS_CLASS_FULLNAME = "django.db.models.options.Options"
|
||||||
HTTPREQUEST_CLASS_FULLNAME = 'django.http.request.HttpRequest'
|
HTTPREQUEST_CLASS_FULLNAME = "django.http.request.HttpRequest"
|
||||||
|
|
||||||
F_EXPRESSION_FULLNAME = 'django.db.models.expressions.F'
|
F_EXPRESSION_FULLNAME = "django.db.models.expressions.F"
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from typing import (
|
from typing import TYPE_CHECKING, Any, Dict, Iterable, Iterator, List, Optional, Set, Tuple, Union
|
||||||
TYPE_CHECKING, Any, Dict, Iterable, Iterator, List, Optional, Set, Tuple, Union,
|
|
||||||
)
|
|
||||||
|
|
||||||
from django.db.models.fields import Field
|
from django.db.models.fields import Field
|
||||||
from django.db.models.fields.related import RelatedField
|
from django.db.models.fields.related import RelatedField
|
||||||
@@ -10,11 +8,31 @@ from mypy import checker
|
|||||||
from mypy.checker import TypeChecker
|
from mypy.checker import TypeChecker
|
||||||
from mypy.mro import calculate_mro
|
from mypy.mro import calculate_mro
|
||||||
from mypy.nodes import (
|
from mypy.nodes import (
|
||||||
GDEF, MDEF, Argument, Block, ClassDef, Expression, FuncDef, MemberExpr, MypyFile, NameExpr, PlaceholderNode,
|
GDEF,
|
||||||
StrExpr, SymbolNode, SymbolTable, SymbolTableNode, TypeInfo, Var,
|
MDEF,
|
||||||
|
Argument,
|
||||||
|
Block,
|
||||||
|
ClassDef,
|
||||||
|
Expression,
|
||||||
|
FuncDef,
|
||||||
|
MemberExpr,
|
||||||
|
MypyFile,
|
||||||
|
NameExpr,
|
||||||
|
PlaceholderNode,
|
||||||
|
StrExpr,
|
||||||
|
SymbolNode,
|
||||||
|
SymbolTable,
|
||||||
|
SymbolTableNode,
|
||||||
|
TypeInfo,
|
||||||
|
Var,
|
||||||
)
|
)
|
||||||
from mypy.plugin import (
|
from mypy.plugin import (
|
||||||
AttributeContext, CheckerPluginInterface, ClassDefContext, DynamicClassDefContext, FunctionContext, MethodContext,
|
AttributeContext,
|
||||||
|
CheckerPluginInterface,
|
||||||
|
ClassDefContext,
|
||||||
|
DynamicClassDefContext,
|
||||||
|
FunctionContext,
|
||||||
|
MethodContext,
|
||||||
)
|
)
|
||||||
from mypy.plugins.common import add_method
|
from mypy.plugins.common import add_method
|
||||||
from mypy.semanal import SemanticAnalyzer
|
from mypy.semanal import SemanticAnalyzer
|
||||||
@@ -29,7 +47,7 @@ if TYPE_CHECKING:
|
|||||||
|
|
||||||
|
|
||||||
def get_django_metadata(model_info: TypeInfo) -> Dict[str, Any]:
|
def get_django_metadata(model_info: TypeInfo) -> Dict[str, Any]:
|
||||||
return model_info.metadata.setdefault('django', {})
|
return model_info.metadata.setdefault("django", {})
|
||||||
|
|
||||||
|
|
||||||
class IncompleteDefnException(Exception):
|
class IncompleteDefnException(Exception):
|
||||||
@@ -37,9 +55,9 @@ class IncompleteDefnException(Exception):
|
|||||||
|
|
||||||
|
|
||||||
def lookup_fully_qualified_sym(fullname: str, all_modules: Dict[str, MypyFile]) -> Optional[SymbolTableNode]:
|
def lookup_fully_qualified_sym(fullname: str, all_modules: Dict[str, MypyFile]) -> Optional[SymbolTableNode]:
|
||||||
if '.' not in fullname:
|
if "." not in fullname:
|
||||||
return None
|
return None
|
||||||
module, cls_name = fullname.rsplit('.', 1)
|
module, cls_name = fullname.rsplit(".", 1)
|
||||||
|
|
||||||
module_file = all_modules.get(module)
|
module_file = all_modules.get(module)
|
||||||
if module_file is None:
|
if module_file is None:
|
||||||
@@ -71,12 +89,11 @@ def lookup_class_typeinfo(api: TypeChecker, klass: type) -> Optional[TypeInfo]:
|
|||||||
|
|
||||||
|
|
||||||
def reparametrize_instance(instance: Instance, new_args: List[MypyType]) -> Instance:
|
def reparametrize_instance(instance: Instance, new_args: List[MypyType]) -> Instance:
|
||||||
return Instance(instance.type, args=new_args,
|
return Instance(instance.type, args=new_args, line=instance.line, column=instance.column)
|
||||||
line=instance.line, column=instance.column)
|
|
||||||
|
|
||||||
|
|
||||||
def get_class_fullname(klass: type) -> str:
|
def get_class_fullname(klass: type) -> str:
|
||||||
return klass.__module__ + '.' + klass.__qualname__
|
return klass.__module__ + "." + klass.__qualname__
|
||||||
|
|
||||||
|
|
||||||
def get_call_argument_by_name(ctx: Union[FunctionContext, MethodContext], name: str) -> Optional[Expression]:
|
def get_call_argument_by_name(ctx: Union[FunctionContext, MethodContext], name: str) -> Optional[Expression]:
|
||||||
@@ -115,9 +132,9 @@ def make_optional(typ: MypyType) -> MypyType:
|
|||||||
|
|
||||||
def parse_bool(expr: Expression) -> Optional[bool]:
|
def parse_bool(expr: Expression) -> Optional[bool]:
|
||||||
if isinstance(expr, NameExpr):
|
if isinstance(expr, NameExpr):
|
||||||
if expr.fullname == 'builtins.True':
|
if expr.fullname == "builtins.True":
|
||||||
return True
|
return True
|
||||||
if expr.fullname == 'builtins.False':
|
if expr.fullname == "builtins.False":
|
||||||
return False
|
return False
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@@ -164,27 +181,24 @@ def get_field_lookup_exact_type(api: TypeChecker, field: Field) -> MypyType:
|
|||||||
field_info = lookup_class_typeinfo(api, field.__class__)
|
field_info = lookup_class_typeinfo(api, field.__class__)
|
||||||
if field_info is None:
|
if field_info is None:
|
||||||
return AnyType(TypeOfAny.explicit)
|
return AnyType(TypeOfAny.explicit)
|
||||||
return get_private_descriptor_type(field_info, '_pyi_lookup_exact_type',
|
return get_private_descriptor_type(field_info, "_pyi_lookup_exact_type", is_nullable=field.null)
|
||||||
is_nullable=field.null)
|
|
||||||
|
|
||||||
|
|
||||||
def get_nested_meta_node_for_current_class(info: TypeInfo) -> Optional[TypeInfo]:
|
def get_nested_meta_node_for_current_class(info: TypeInfo) -> Optional[TypeInfo]:
|
||||||
metaclass_sym = info.names.get('Meta')
|
metaclass_sym = info.names.get("Meta")
|
||||||
if metaclass_sym is not None and isinstance(metaclass_sym.node, TypeInfo):
|
if metaclass_sym is not None and isinstance(metaclass_sym.node, TypeInfo):
|
||||||
return metaclass_sym.node
|
return metaclass_sym.node
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def add_new_class_for_module(module: MypyFile,
|
def add_new_class_for_module(
|
||||||
name: str,
|
module: MypyFile, name: str, bases: List[Instance], fields: Optional[Dict[str, MypyType]] = None
|
||||||
bases: List[Instance],
|
) -> TypeInfo:
|
||||||
fields: Optional[Dict[str, MypyType]] = None
|
|
||||||
) -> TypeInfo:
|
|
||||||
new_class_unique_name = checker.gen_unique_name(name, module.names)
|
new_class_unique_name = checker.gen_unique_name(name, module.names)
|
||||||
|
|
||||||
# make new class expression
|
# make new class expression
|
||||||
classdef = ClassDef(new_class_unique_name, Block([]))
|
classdef = ClassDef(new_class_unique_name, Block([]))
|
||||||
classdef.fullname = module.fullname + '.' + new_class_unique_name
|
classdef.fullname = module.fullname + "." + new_class_unique_name
|
||||||
|
|
||||||
# make new TypeInfo
|
# make new TypeInfo
|
||||||
new_typeinfo = TypeInfo(SymbolTable(), classdef, module.fullname)
|
new_typeinfo = TypeInfo(SymbolTable(), classdef, module.fullname)
|
||||||
@@ -197,7 +211,7 @@ def add_new_class_for_module(module: MypyFile,
|
|||||||
for field_name, field_type in fields.items():
|
for field_name, field_type in fields.items():
|
||||||
var = Var(field_name, type=field_type)
|
var = Var(field_name, type=field_type)
|
||||||
var.info = new_typeinfo
|
var.info = new_typeinfo
|
||||||
var._fullname = new_typeinfo.fullname + '.' + field_name
|
var._fullname = new_typeinfo.fullname + "." + field_name
|
||||||
new_typeinfo.names[field_name] = SymbolTableNode(MDEF, var, plugin_generated=True)
|
new_typeinfo.names[field_name] = SymbolTableNode(MDEF, var, plugin_generated=True)
|
||||||
|
|
||||||
classdef.info = new_typeinfo
|
classdef.info = new_typeinfo
|
||||||
@@ -215,18 +229,17 @@ def get_current_module(api: TypeChecker) -> MypyFile:
|
|||||||
return current_module
|
return current_module
|
||||||
|
|
||||||
|
|
||||||
def make_oneoff_named_tuple(api: TypeChecker, name: str, fields: 'OrderedDict[str, MypyType]') -> TupleType:
|
def make_oneoff_named_tuple(api: TypeChecker, name: str, fields: "OrderedDict[str, MypyType]") -> TupleType:
|
||||||
current_module = get_current_module(api)
|
current_module = get_current_module(api)
|
||||||
namedtuple_info = add_new_class_for_module(current_module, name,
|
namedtuple_info = add_new_class_for_module(
|
||||||
bases=[api.named_generic_type('typing.NamedTuple', [])],
|
current_module, name, bases=[api.named_generic_type("typing.NamedTuple", [])], fields=fields
|
||||||
fields=fields)
|
)
|
||||||
return TupleType(list(fields.values()), fallback=Instance(namedtuple_info, []))
|
return TupleType(list(fields.values()), fallback=Instance(namedtuple_info, []))
|
||||||
|
|
||||||
|
|
||||||
def make_tuple(api: 'TypeChecker', fields: List[MypyType]) -> TupleType:
|
def make_tuple(api: "TypeChecker", fields: List[MypyType]) -> TupleType:
|
||||||
# fallback for tuples is any builtins.tuple instance
|
# fallback for tuples is any builtins.tuple instance
|
||||||
fallback = api.named_generic_type('builtins.tuple',
|
fallback = api.named_generic_type("builtins.tuple", [AnyType(TypeOfAny.special_form)])
|
||||||
[AnyType(TypeOfAny.special_form)])
|
|
||||||
return TupleType(fields, fallback=fallback)
|
return TupleType(fields, fallback=fallback)
|
||||||
|
|
||||||
|
|
||||||
@@ -235,8 +248,7 @@ def convert_any_to_type(typ: MypyType, referred_to_type: MypyType) -> MypyType:
|
|||||||
converted_items = []
|
converted_items = []
|
||||||
for item in typ.items:
|
for item in typ.items:
|
||||||
converted_items.append(convert_any_to_type(item, referred_to_type))
|
converted_items.append(convert_any_to_type(item, referred_to_type))
|
||||||
return UnionType.make_union(converted_items,
|
return UnionType.make_union(converted_items, line=typ.line, column=typ.column)
|
||||||
line=typ.line, column=typ.column)
|
|
||||||
if isinstance(typ, Instance):
|
if isinstance(typ, Instance):
|
||||||
args = []
|
args = []
|
||||||
for default_arg in typ.args:
|
for default_arg in typ.args:
|
||||||
@@ -252,21 +264,22 @@ def convert_any_to_type(typ: MypyType, referred_to_type: MypyType) -> MypyType:
|
|||||||
return typ
|
return typ
|
||||||
|
|
||||||
|
|
||||||
def make_typeddict(api: CheckerPluginInterface, fields: 'OrderedDict[str, MypyType]',
|
def make_typeddict(
|
||||||
required_keys: Set[str]) -> TypedDictType:
|
api: CheckerPluginInterface, fields: "OrderedDict[str, MypyType]", required_keys: Set[str]
|
||||||
object_type = api.named_generic_type('mypy_extensions._TypedDict', [])
|
) -> TypedDictType:
|
||||||
|
object_type = api.named_generic_type("mypy_extensions._TypedDict", [])
|
||||||
typed_dict_type = TypedDictType(fields, required_keys=required_keys, fallback=object_type)
|
typed_dict_type = TypedDictType(fields, required_keys=required_keys, fallback=object_type)
|
||||||
return typed_dict_type
|
return typed_dict_type
|
||||||
|
|
||||||
|
|
||||||
def resolve_string_attribute_value(attr_expr: Expression, django_context: 'DjangoContext') -> Optional[str]:
|
def resolve_string_attribute_value(attr_expr: Expression, django_context: "DjangoContext") -> Optional[str]:
|
||||||
if isinstance(attr_expr, StrExpr):
|
if isinstance(attr_expr, StrExpr):
|
||||||
return attr_expr.value
|
return attr_expr.value
|
||||||
|
|
||||||
# support extracting from settings, in general case it's unresolvable yet
|
# support extracting from settings, in general case it's unresolvable yet
|
||||||
if isinstance(attr_expr, MemberExpr):
|
if isinstance(attr_expr, MemberExpr):
|
||||||
member_name = attr_expr.name
|
member_name = attr_expr.name
|
||||||
if isinstance(attr_expr.expr, NameExpr) and attr_expr.expr.fullname == 'django.conf.settings':
|
if isinstance(attr_expr.expr, NameExpr) and attr_expr.expr.fullname == "django.conf.settings":
|
||||||
if hasattr(django_context.settings, member_name):
|
if hasattr(django_context.settings, member_name):
|
||||||
return getattr(django_context.settings, member_name)
|
return getattr(django_context.settings, member_name)
|
||||||
return None
|
return None
|
||||||
@@ -274,27 +287,27 @@ def resolve_string_attribute_value(attr_expr: Expression, django_context: 'Djang
|
|||||||
|
|
||||||
def get_semanal_api(ctx: Union[ClassDefContext, DynamicClassDefContext]) -> SemanticAnalyzer:
|
def get_semanal_api(ctx: Union[ClassDefContext, DynamicClassDefContext]) -> SemanticAnalyzer:
|
||||||
if not isinstance(ctx.api, SemanticAnalyzer):
|
if not isinstance(ctx.api, SemanticAnalyzer):
|
||||||
raise ValueError('Not a SemanticAnalyzer')
|
raise ValueError("Not a SemanticAnalyzer")
|
||||||
return ctx.api
|
return ctx.api
|
||||||
|
|
||||||
|
|
||||||
def get_typechecker_api(ctx: Union[AttributeContext, MethodContext, FunctionContext]) -> TypeChecker:
|
def get_typechecker_api(ctx: Union[AttributeContext, MethodContext, FunctionContext]) -> TypeChecker:
|
||||||
if not isinstance(ctx.api, TypeChecker):
|
if not isinstance(ctx.api, TypeChecker):
|
||||||
raise ValueError('Not a TypeChecker')
|
raise ValueError("Not a TypeChecker")
|
||||||
return ctx.api
|
return ctx.api
|
||||||
|
|
||||||
|
|
||||||
def is_model_subclass_info(info: TypeInfo, django_context: 'DjangoContext') -> bool:
|
def is_model_subclass_info(info: TypeInfo, django_context: "DjangoContext") -> bool:
|
||||||
return (info.fullname in django_context.all_registered_model_class_fullnames
|
return info.fullname in django_context.all_registered_model_class_fullnames or info.has_base(
|
||||||
or info.has_base(fullnames.MODEL_CLASS_FULLNAME))
|
fullnames.MODEL_CLASS_FULLNAME
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def check_types_compatible(ctx: Union[FunctionContext, MethodContext],
|
def check_types_compatible(
|
||||||
*, expected_type: MypyType, actual_type: MypyType, error_message: str) -> None:
|
ctx: Union[FunctionContext, MethodContext], *, expected_type: MypyType, actual_type: MypyType, error_message: str
|
||||||
|
) -> None:
|
||||||
api = get_typechecker_api(ctx)
|
api = get_typechecker_api(ctx)
|
||||||
api.check_subtype(actual_type, expected_type,
|
api.check_subtype(actual_type, expected_type, ctx.context, error_message, "got", "expected")
|
||||||
ctx.context, error_message,
|
|
||||||
'got', 'expected')
|
|
||||||
|
|
||||||
|
|
||||||
def add_new_sym_for_info(info: TypeInfo, *, name: str, sym_type: MypyType) -> None:
|
def add_new_sym_for_info(info: TypeInfo, *, name: str, sym_type: MypyType) -> None:
|
||||||
@@ -302,11 +315,10 @@ def add_new_sym_for_info(info: TypeInfo, *, name: str, sym_type: MypyType) -> No
|
|||||||
var = Var(name=name, type=sym_type)
|
var = Var(name=name, type=sym_type)
|
||||||
# var.info: type of the object variable is bound to
|
# var.info: type of the object variable is bound to
|
||||||
var.info = info
|
var.info = info
|
||||||
var._fullname = info.fullname + '.' + name
|
var._fullname = info.fullname + "." + name
|
||||||
var.is_initialized_in_class = True
|
var.is_initialized_in_class = True
|
||||||
var.is_inferred = True
|
var.is_inferred = True
|
||||||
info.names[name] = SymbolTableNode(MDEF, var,
|
info.names[name] = SymbolTableNode(MDEF, var, plugin_generated=True)
|
||||||
plugin_generated=True)
|
|
||||||
|
|
||||||
|
|
||||||
def build_unannotated_method_args(method_node: FuncDef) -> Tuple[List[Argument], MypyType]:
|
def build_unannotated_method_args(method_node: FuncDef) -> Tuple[List[Argument], MypyType]:
|
||||||
@@ -322,8 +334,9 @@ def build_unannotated_method_args(method_node: FuncDef) -> Tuple[List[Argument],
|
|||||||
return prepared_arguments, return_type
|
return prepared_arguments, return_type
|
||||||
|
|
||||||
|
|
||||||
def copy_method_to_another_class(ctx: ClassDefContext, self_type: Instance,
|
def copy_method_to_another_class(
|
||||||
new_method_name: str, method_node: FuncDef) -> None:
|
ctx: ClassDefContext, self_type: Instance, new_method_name: str, method_node: FuncDef
|
||||||
|
) -> None:
|
||||||
semanal_api = get_semanal_api(ctx)
|
semanal_api = get_semanal_api(ctx)
|
||||||
if method_node.type is None:
|
if method_node.type is None:
|
||||||
if not semanal_api.final_iteration:
|
if not semanal_api.final_iteration:
|
||||||
@@ -331,11 +344,7 @@ def copy_method_to_another_class(ctx: ClassDefContext, self_type: Instance,
|
|||||||
return
|
return
|
||||||
|
|
||||||
arguments, return_type = build_unannotated_method_args(method_node)
|
arguments, return_type = build_unannotated_method_args(method_node)
|
||||||
add_method(ctx,
|
add_method(ctx, new_method_name, args=arguments, return_type=return_type, self_type=self_type)
|
||||||
new_method_name,
|
|
||||||
args=arguments,
|
|
||||||
return_type=return_type,
|
|
||||||
self_type=self_type)
|
|
||||||
return
|
return
|
||||||
|
|
||||||
method_type = method_node.type
|
method_type = method_node.type
|
||||||
@@ -345,17 +354,21 @@ def copy_method_to_another_class(ctx: ClassDefContext, self_type: Instance,
|
|||||||
return
|
return
|
||||||
|
|
||||||
arguments = []
|
arguments = []
|
||||||
bound_return_type = semanal_api.anal_type(method_type.ret_type,
|
bound_return_type = semanal_api.anal_type(method_type.ret_type, allow_placeholder=True)
|
||||||
allow_placeholder=True)
|
|
||||||
|
|
||||||
assert bound_return_type is not None
|
assert bound_return_type is not None
|
||||||
|
|
||||||
if isinstance(bound_return_type, PlaceholderNode):
|
if isinstance(bound_return_type, PlaceholderNode):
|
||||||
return
|
return
|
||||||
|
|
||||||
for arg_name, arg_type, original_argument in zip(method_type.arg_names[1:],
|
try:
|
||||||
method_type.arg_types[1:],
|
original_arguments = method_node.arguments[1:]
|
||||||
method_node.arguments[1:]):
|
except AttributeError:
|
||||||
|
original_arguments = []
|
||||||
|
|
||||||
|
for arg_name, arg_type, original_argument in zip(
|
||||||
|
method_type.arg_names[1:], method_type.arg_types[1:], original_arguments
|
||||||
|
):
|
||||||
bound_arg_type = semanal_api.anal_type(arg_type, allow_placeholder=True)
|
bound_arg_type = semanal_api.anal_type(arg_type, allow_placeholder=True)
|
||||||
if bound_arg_type is None and not semanal_api.final_iteration:
|
if bound_arg_type is None and not semanal_api.final_iteration:
|
||||||
semanal_api.defer()
|
semanal_api.defer()
|
||||||
@@ -366,19 +379,16 @@ def copy_method_to_another_class(ctx: ClassDefContext, self_type: Instance,
|
|||||||
if isinstance(bound_arg_type, PlaceholderNode):
|
if isinstance(bound_arg_type, PlaceholderNode):
|
||||||
return
|
return
|
||||||
|
|
||||||
var = Var(name=original_argument.variable.name,
|
var = Var(name=original_argument.variable.name, type=arg_type)
|
||||||
type=arg_type)
|
|
||||||
var.line = original_argument.variable.line
|
var.line = original_argument.variable.line
|
||||||
var.column = original_argument.variable.column
|
var.column = original_argument.variable.column
|
||||||
argument = Argument(variable=var,
|
argument = Argument(
|
||||||
|
variable=var,
|
||||||
type_annotation=bound_arg_type,
|
type_annotation=bound_arg_type,
|
||||||
initializer=original_argument.initializer,
|
initializer=original_argument.initializer,
|
||||||
kind=original_argument.kind)
|
kind=original_argument.kind,
|
||||||
|
)
|
||||||
argument.set_line(original_argument)
|
argument.set_line(original_argument)
|
||||||
arguments.append(argument)
|
arguments.append(argument)
|
||||||
|
|
||||||
add_method(ctx,
|
add_method(ctx, new_method_name, args=arguments, return_type=bound_return_type, self_type=self_type)
|
||||||
new_method_name,
|
|
||||||
args=arguments,
|
|
||||||
return_type=bound_return_type,
|
|
||||||
self_type=self_type)
|
|
||||||
|
|||||||
@@ -8,28 +8,28 @@ from mypy.modulefinder import mypy_path
|
|||||||
from mypy.nodes import MypyFile, TypeInfo
|
from mypy.nodes import MypyFile, TypeInfo
|
||||||
from mypy.options import Options
|
from mypy.options import Options
|
||||||
from mypy.plugin import (
|
from mypy.plugin import (
|
||||||
AttributeContext, ClassDefContext, DynamicClassDefContext, FunctionContext, MethodContext, Plugin,
|
AttributeContext,
|
||||||
|
ClassDefContext,
|
||||||
|
DynamicClassDefContext,
|
||||||
|
FunctionContext,
|
||||||
|
MethodContext,
|
||||||
|
Plugin,
|
||||||
)
|
)
|
||||||
from mypy.types import Type as MypyType
|
from mypy.types import Type as MypyType
|
||||||
|
|
||||||
import mypy_django_plugin.transformers.orm_lookups
|
import mypy_django_plugin.transformers.orm_lookups
|
||||||
from mypy_django_plugin.django.context import DjangoContext
|
from mypy_django_plugin.django.context import DjangoContext
|
||||||
from mypy_django_plugin.lib import fullnames, helpers
|
from mypy_django_plugin.lib import fullnames, helpers
|
||||||
from mypy_django_plugin.transformers import (
|
from mypy_django_plugin.transformers import fields, forms, init_create, meta, querysets, request, settings
|
||||||
fields, forms, init_create, meta, querysets, request, settings,
|
from mypy_django_plugin.transformers.managers import create_new_manager_class_from_from_queryset_method
|
||||||
)
|
from mypy_django_plugin.transformers.models import process_model_class, set_auth_user_model_boolean_fields
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
def transform_model_class(ctx: ClassDefContext,
|
def transform_model_class(ctx: ClassDefContext, django_context: DjangoContext) -> None:
|
||||||
django_context: DjangoContext) -> None:
|
|
||||||
sym = ctx.api.lookup_fully_qualified_or_none(fullnames.MODEL_CLASS_FULLNAME)
|
sym = ctx.api.lookup_fully_qualified_or_none(fullnames.MODEL_CLASS_FULLNAME)
|
||||||
|
|
||||||
if sym is not None and isinstance(sym.node, TypeInfo):
|
if sym is not None and isinstance(sym.node, TypeInfo):
|
||||||
helpers.get_django_metadata(sym.node)['model_bases'][ctx.cls.fullname] = 1
|
helpers.get_django_metadata(sym.node)["model_bases"][ctx.cls.fullname] = 1
|
||||||
else:
|
else:
|
||||||
if not ctx.api.final_iteration:
|
if not ctx.api.final_iteration:
|
||||||
ctx.api.defer()
|
ctx.api.defer()
|
||||||
@@ -41,7 +41,7 @@ def transform_model_class(ctx: ClassDefContext,
|
|||||||
def transform_form_class(ctx: ClassDefContext) -> None:
|
def transform_form_class(ctx: ClassDefContext) -> None:
|
||||||
sym = ctx.api.lookup_fully_qualified_or_none(fullnames.BASEFORM_CLASS_FULLNAME)
|
sym = ctx.api.lookup_fully_qualified_or_none(fullnames.BASEFORM_CLASS_FULLNAME)
|
||||||
if sym is not None and isinstance(sym.node, TypeInfo):
|
if sym is not None and isinstance(sym.node, TypeInfo):
|
||||||
helpers.get_django_metadata(sym.node)['baseform_bases'][ctx.cls.fullname] = 1
|
helpers.get_django_metadata(sym.node)["baseform_bases"][ctx.cls.fullname] = 1
|
||||||
|
|
||||||
forms.make_meta_nested_class_inherit_from_any(ctx)
|
forms.make_meta_nested_class_inherit_from_any(ctx)
|
||||||
|
|
||||||
@@ -49,11 +49,10 @@ def transform_form_class(ctx: ClassDefContext) -> None:
|
|||||||
def add_new_manager_base(ctx: ClassDefContext) -> None:
|
def add_new_manager_base(ctx: ClassDefContext) -> None:
|
||||||
sym = ctx.api.lookup_fully_qualified_or_none(fullnames.MANAGER_CLASS_FULLNAME)
|
sym = ctx.api.lookup_fully_qualified_or_none(fullnames.MANAGER_CLASS_FULLNAME)
|
||||||
if sym is not None and isinstance(sym.node, TypeInfo):
|
if sym is not None and isinstance(sym.node, TypeInfo):
|
||||||
helpers.get_django_metadata(sym.node)['manager_bases'][ctx.cls.fullname] = 1
|
helpers.get_django_metadata(sym.node)["manager_bases"][ctx.cls.fullname] = 1
|
||||||
|
|
||||||
|
|
||||||
def extract_django_settings_module(config_file_path: Optional[str]) -> str:
|
def extract_django_settings_module(config_file_path: Optional[str]) -> str:
|
||||||
|
|
||||||
def exit(error_type: int) -> NoReturn:
|
def exit(error_type: int) -> NoReturn:
|
||||||
"""Using mypy's argument parser, raise `SystemExit` to fail hard if validation fails.
|
"""Using mypy's argument parser, raise `SystemExit` to fail hard if validation fails.
|
||||||
|
|
||||||
@@ -69,24 +68,29 @@ def extract_django_settings_module(config_file_path: Optional[str]) -> str:
|
|||||||
[mypy.plugins.django_stubs]
|
[mypy.plugins.django_stubs]
|
||||||
django_settings_module: str (required)
|
django_settings_module: str (required)
|
||||||
...
|
...
|
||||||
""".replace("\n" + 8 * " ", "\n")
|
""".replace(
|
||||||
handler = CapturableArgumentParser(prog='(django-stubs) mypy', usage=usage)
|
"\n" + 8 * " ", "\n"
|
||||||
messages = {1: 'mypy config file is not specified or found',
|
)
|
||||||
2: 'no section [mypy.plugins.django-stubs]',
|
handler = CapturableArgumentParser(prog="(django-stubs) mypy", usage=usage)
|
||||||
3: 'the setting is not provided'}
|
messages = {
|
||||||
|
1: "mypy config file is not specified or found",
|
||||||
|
2: "no section [mypy.plugins.django-stubs]",
|
||||||
|
3: "the setting is not provided",
|
||||||
|
}
|
||||||
handler.error("'django_settings_module' is not set: " + messages[error_type])
|
handler.error("'django_settings_module' is not set: " + messages[error_type])
|
||||||
|
|
||||||
parser = configparser.ConfigParser()
|
parser = configparser.ConfigParser()
|
||||||
try:
|
try:
|
||||||
parser.read_file(open(cast(str, config_file_path), 'r'), source=config_file_path)
|
with open(cast(str, config_file_path)) as handle:
|
||||||
|
parser.read_file(handle, source=config_file_path)
|
||||||
except (IsADirectoryError, OSError):
|
except (IsADirectoryError, OSError):
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
section = 'mypy.plugins.django-stubs'
|
section = "mypy.plugins.django-stubs"
|
||||||
if not parser.has_section(section):
|
if not parser.has_section(section):
|
||||||
exit(2)
|
exit(2)
|
||||||
settings = parser.get(section, 'django_settings_module', fallback=None) or exit(3)
|
settings = parser.get(section, "django_settings_module", fallback=None) or exit(3)
|
||||||
return cast(str, settings).strip('\'"')
|
return cast(str, settings).strip("'\"")
|
||||||
|
|
||||||
|
|
||||||
class NewSemanalDjangoPlugin(Plugin):
|
class NewSemanalDjangoPlugin(Plugin):
|
||||||
@@ -102,34 +106,41 @@ class NewSemanalDjangoPlugin(Plugin):
|
|||||||
def _get_current_queryset_bases(self) -> Dict[str, int]:
|
def _get_current_queryset_bases(self) -> Dict[str, int]:
|
||||||
model_sym = self.lookup_fully_qualified(fullnames.QUERYSET_CLASS_FULLNAME)
|
model_sym = self.lookup_fully_qualified(fullnames.QUERYSET_CLASS_FULLNAME)
|
||||||
if model_sym is not None and isinstance(model_sym.node, TypeInfo):
|
if model_sym is not None and isinstance(model_sym.node, TypeInfo):
|
||||||
return (helpers.get_django_metadata(model_sym.node)
|
return helpers.get_django_metadata(model_sym.node).setdefault(
|
||||||
.setdefault('queryset_bases', {fullnames.QUERYSET_CLASS_FULLNAME: 1}))
|
"queryset_bases", {fullnames.QUERYSET_CLASS_FULLNAME: 1}
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
def _get_current_manager_bases(self) -> Dict[str, int]:
|
def _get_current_manager_bases(self) -> Dict[str, int]:
|
||||||
model_sym = self.lookup_fully_qualified(fullnames.MANAGER_CLASS_FULLNAME)
|
model_sym = self.lookup_fully_qualified(fullnames.MANAGER_CLASS_FULLNAME)
|
||||||
if model_sym is not None and isinstance(model_sym.node, TypeInfo):
|
if model_sym is not None and isinstance(model_sym.node, TypeInfo):
|
||||||
return (helpers.get_django_metadata(model_sym.node)
|
return helpers.get_django_metadata(model_sym.node).setdefault(
|
||||||
.setdefault('manager_bases', {fullnames.MANAGER_CLASS_FULLNAME: 1}))
|
"manager_bases", {fullnames.MANAGER_CLASS_FULLNAME: 1}
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
def _get_current_model_bases(self) -> Dict[str, int]:
|
def _get_current_model_bases(self) -> Dict[str, int]:
|
||||||
model_sym = self.lookup_fully_qualified(fullnames.MODEL_CLASS_FULLNAME)
|
model_sym = self.lookup_fully_qualified(fullnames.MODEL_CLASS_FULLNAME)
|
||||||
if model_sym is not None and isinstance(model_sym.node, TypeInfo):
|
if model_sym is not None and isinstance(model_sym.node, TypeInfo):
|
||||||
return helpers.get_django_metadata(model_sym.node).setdefault('model_bases',
|
return helpers.get_django_metadata(model_sym.node).setdefault(
|
||||||
{fullnames.MODEL_CLASS_FULLNAME: 1})
|
"model_bases", {fullnames.MODEL_CLASS_FULLNAME: 1}
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
def _get_current_form_bases(self) -> Dict[str, int]:
|
def _get_current_form_bases(self) -> Dict[str, int]:
|
||||||
model_sym = self.lookup_fully_qualified(fullnames.BASEFORM_CLASS_FULLNAME)
|
model_sym = self.lookup_fully_qualified(fullnames.BASEFORM_CLASS_FULLNAME)
|
||||||
if model_sym is not None and isinstance(model_sym.node, TypeInfo):
|
if model_sym is not None and isinstance(model_sym.node, TypeInfo):
|
||||||
return (helpers.get_django_metadata(model_sym.node)
|
return helpers.get_django_metadata(model_sym.node).setdefault(
|
||||||
.setdefault('baseform_bases', {fullnames.BASEFORM_CLASS_FULLNAME: 1,
|
"baseform_bases",
|
||||||
|
{
|
||||||
|
fullnames.BASEFORM_CLASS_FULLNAME: 1,
|
||||||
fullnames.FORM_CLASS_FULLNAME: 1,
|
fullnames.FORM_CLASS_FULLNAME: 1,
|
||||||
fullnames.MODELFORM_CLASS_FULLNAME: 1}))
|
fullnames.MODELFORM_CLASS_FULLNAME: 1,
|
||||||
|
},
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
@@ -144,17 +155,16 @@ class NewSemanalDjangoPlugin(Plugin):
|
|||||||
|
|
||||||
def get_additional_deps(self, file: MypyFile) -> List[Tuple[int, str, int]]:
|
def get_additional_deps(self, file: MypyFile) -> List[Tuple[int, str, int]]:
|
||||||
# for settings
|
# for settings
|
||||||
if file.fullname == 'django.conf' and self.django_context.django_settings_module:
|
if file.fullname == "django.conf" and self.django_context.django_settings_module:
|
||||||
return [self._new_dependency(self.django_context.django_settings_module)]
|
return [self._new_dependency(self.django_context.django_settings_module)]
|
||||||
|
|
||||||
# for values / values_list
|
# for values / values_list
|
||||||
if file.fullname == 'django.db.models':
|
if file.fullname == "django.db.models":
|
||||||
return [self._new_dependency('mypy_extensions'), self._new_dependency('typing')]
|
return [self._new_dependency("mypy_extensions"), self._new_dependency("typing")]
|
||||||
|
|
||||||
# for `get_user_model()`
|
# for `get_user_model()`
|
||||||
if self.django_context.settings:
|
if self.django_context.settings:
|
||||||
if (file.fullname == 'django.contrib.auth'
|
if file.fullname == "django.contrib.auth" or file.fullname in {"django.http", "django.http.request"}:
|
||||||
or file.fullname in {'django.http', 'django.http.request'}):
|
|
||||||
auth_user_model_name = self.django_context.settings.AUTH_USER_MODEL
|
auth_user_model_name = self.django_context.settings.AUTH_USER_MODEL
|
||||||
try:
|
try:
|
||||||
auth_user_module = self.django_context.apps_registry.get_model(auth_user_model_name).__module__
|
auth_user_module = self.django_context.apps_registry.get_model(auth_user_model_name).__module__
|
||||||
@@ -186,9 +196,8 @@ class NewSemanalDjangoPlugin(Plugin):
|
|||||||
deps.add(self._new_dependency(related_model_module))
|
deps.add(self._new_dependency(related_model_module))
|
||||||
return list(deps)
|
return list(deps)
|
||||||
|
|
||||||
def get_function_hook(self, fullname: str
|
def get_function_hook(self, fullname: str) -> Optional[Callable[[FunctionContext], MypyType]]:
|
||||||
) -> Optional[Callable[[FunctionContext], MypyType]]:
|
if fullname == "django.contrib.auth.get_user_model":
|
||||||
if fullname == 'django.contrib.auth.get_user_model':
|
|
||||||
return partial(settings.get_user_model_hook, django_context=self.django_context)
|
return partial(settings.get_user_model_hook, django_context=self.django_context)
|
||||||
|
|
||||||
manager_bases = self._get_current_manager_bases()
|
manager_bases = self._get_current_manager_bases()
|
||||||
@@ -204,46 +213,48 @@ class NewSemanalDjangoPlugin(Plugin):
|
|||||||
return partial(init_create.redefine_and_typecheck_model_init, django_context=self.django_context)
|
return partial(init_create.redefine_and_typecheck_model_init, django_context=self.django_context)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_method_hook(self, fullname: str
|
def get_method_hook(self, fullname: str) -> Optional[Callable[[MethodContext], MypyType]]:
|
||||||
) -> Optional[Callable[[MethodContext], MypyType]]:
|
class_fullname, _, method_name = fullname.rpartition(".")
|
||||||
class_fullname, _, method_name = fullname.rpartition('.')
|
if method_name == "get_form_class":
|
||||||
if method_name == 'get_form_class':
|
|
||||||
info = self._get_typeinfo_or_none(class_fullname)
|
info = self._get_typeinfo_or_none(class_fullname)
|
||||||
if info and info.has_base(fullnames.FORM_MIXIN_CLASS_FULLNAME):
|
if info and info.has_base(fullnames.FORM_MIXIN_CLASS_FULLNAME):
|
||||||
return forms.extract_proper_type_for_get_form_class
|
return forms.extract_proper_type_for_get_form_class
|
||||||
|
|
||||||
if method_name == 'get_form':
|
if method_name == "get_form":
|
||||||
info = self._get_typeinfo_or_none(class_fullname)
|
info = self._get_typeinfo_or_none(class_fullname)
|
||||||
if info and info.has_base(fullnames.FORM_MIXIN_CLASS_FULLNAME):
|
if info and info.has_base(fullnames.FORM_MIXIN_CLASS_FULLNAME):
|
||||||
return forms.extract_proper_type_for_get_form
|
return forms.extract_proper_type_for_get_form
|
||||||
|
|
||||||
if method_name == 'values':
|
if method_name == "values":
|
||||||
info = self._get_typeinfo_or_none(class_fullname)
|
info = self._get_typeinfo_or_none(class_fullname)
|
||||||
if info and info.has_base(fullnames.QUERYSET_CLASS_FULLNAME):
|
if info and info.has_base(fullnames.QUERYSET_CLASS_FULLNAME):
|
||||||
return partial(querysets.extract_proper_type_queryset_values, django_context=self.django_context)
|
return partial(querysets.extract_proper_type_queryset_values, django_context=self.django_context)
|
||||||
|
|
||||||
if method_name == 'values_list':
|
if method_name == "values_list":
|
||||||
info = self._get_typeinfo_or_none(class_fullname)
|
info = self._get_typeinfo_or_none(class_fullname)
|
||||||
if info and info.has_base(fullnames.QUERYSET_CLASS_FULLNAME):
|
if info and info.has_base(fullnames.QUERYSET_CLASS_FULLNAME):
|
||||||
return partial(querysets.extract_proper_type_queryset_values_list, django_context=self.django_context)
|
return partial(querysets.extract_proper_type_queryset_values_list, django_context=self.django_context)
|
||||||
|
|
||||||
if method_name == 'get_field':
|
if method_name == "get_field":
|
||||||
info = self._get_typeinfo_or_none(class_fullname)
|
info = self._get_typeinfo_or_none(class_fullname)
|
||||||
if info and info.has_base(fullnames.OPTIONS_CLASS_FULLNAME):
|
if info and info.has_base(fullnames.OPTIONS_CLASS_FULLNAME):
|
||||||
return partial(meta.return_proper_field_type_from_get_field, django_context=self.django_context)
|
return partial(meta.return_proper_field_type_from_get_field, django_context=self.django_context)
|
||||||
|
|
||||||
manager_classes = self._get_current_manager_bases()
|
manager_classes = self._get_current_manager_bases()
|
||||||
if class_fullname in manager_classes and method_name == 'create':
|
if class_fullname in manager_classes and method_name == "create":
|
||||||
return partial(init_create.redefine_and_typecheck_model_create, django_context=self.django_context)
|
return partial(init_create.redefine_and_typecheck_model_create, django_context=self.django_context)
|
||||||
if class_fullname in manager_classes and method_name in {'filter', 'get', 'exclude'}:
|
if class_fullname in manager_classes and method_name in {"filter", "get", "exclude"}:
|
||||||
return partial(mypy_django_plugin.transformers.orm_lookups.typecheck_queryset_filter,
|
return partial(
|
||||||
django_context=self.django_context)
|
mypy_django_plugin.transformers.orm_lookups.typecheck_queryset_filter,
|
||||||
|
django_context=self.django_context,
|
||||||
|
)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_base_class_hook(self, fullname: str
|
def get_base_class_hook(self, fullname: str) -> Optional[Callable[[ClassDefContext], None]]:
|
||||||
) -> Optional[Callable[[ClassDefContext], None]]:
|
if (
|
||||||
if (fullname in self.django_context.all_registered_model_class_fullnames
|
fullname in self.django_context.all_registered_model_class_fullnames
|
||||||
or fullname in self._get_current_model_bases()):
|
or fullname in self._get_current_model_bases()
|
||||||
|
):
|
||||||
return partial(transform_model_class, django_context=self.django_context)
|
return partial(transform_model_class, django_context=self.django_context)
|
||||||
|
|
||||||
if fullname in self._get_current_manager_bases():
|
if fullname in self._get_current_manager_bases():
|
||||||
@@ -253,22 +264,23 @@ class NewSemanalDjangoPlugin(Plugin):
|
|||||||
return transform_form_class
|
return transform_form_class
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_attribute_hook(self, fullname: str
|
def get_attribute_hook(self, fullname: str) -> Optional[Callable[[AttributeContext], MypyType]]:
|
||||||
) -> Optional[Callable[[AttributeContext], MypyType]]:
|
class_name, _, attr_name = fullname.rpartition(".")
|
||||||
class_name, _, attr_name = fullname.rpartition('.')
|
|
||||||
if class_name == fullnames.DUMMY_SETTINGS_BASE_CLASS:
|
if class_name == fullnames.DUMMY_SETTINGS_BASE_CLASS:
|
||||||
return partial(settings.get_type_of_settings_attribute,
|
return partial(settings.get_type_of_settings_attribute, django_context=self.django_context)
|
||||||
django_context=self.django_context)
|
|
||||||
|
|
||||||
info = self._get_typeinfo_or_none(class_name)
|
info = self._get_typeinfo_or_none(class_name)
|
||||||
if info and info.has_base(fullnames.HTTPREQUEST_CLASS_FULLNAME) and attr_name == 'user':
|
if info and info.has_base(fullnames.PERMISSION_MIXIN_CLASS_FULLNAME) and attr_name == "is_superuser":
|
||||||
|
return partial(set_auth_user_model_boolean_fields, django_context=self.django_context)
|
||||||
|
if info and info.has_base(fullnames.HTTPREQUEST_CLASS_FULLNAME) and attr_name == "user":
|
||||||
return partial(request.set_auth_user_model_as_type_for_request_user, django_context=self.django_context)
|
return partial(request.set_auth_user_model_as_type_for_request_user, django_context=self.django_context)
|
||||||
|
if info and info.has_base(fullnames.ABSTRACT_USER_MODEL_FULLNAME) and attr_name in ("is_staff", "is_active"):
|
||||||
|
return partial(set_auth_user_model_boolean_fields, django_context=self.django_context)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_dynamic_class_hook(self, fullname: str
|
def get_dynamic_class_hook(self, fullname: str) -> Optional[Callable[[DynamicClassDefContext], None]]:
|
||||||
) -> Optional[Callable[[DynamicClassDefContext], None]]:
|
if fullname.endswith("from_queryset"):
|
||||||
if fullname.endswith('from_queryset'):
|
class_name, _, _ = fullname.rpartition(".")
|
||||||
class_name, _, _ = fullname.rpartition('.')
|
|
||||||
info = self._get_typeinfo_or_none(class_name)
|
info = self._get_typeinfo_or_none(class_name)
|
||||||
if info and info.has_base(fullnames.BASE_MANAGER_CLASS_FULLNAME):
|
if info and info.has_base(fullnames.BASE_MANAGER_CLASS_FULLNAME):
|
||||||
return create_new_manager_class_from_from_queryset_method
|
return create_new_manager_class_from_from_queryset_method
|
||||||
|
|||||||
@@ -14,8 +14,7 @@ from mypy_django_plugin.lib import fullnames, helpers
|
|||||||
|
|
||||||
def _get_current_field_from_assignment(ctx: FunctionContext, django_context: DjangoContext) -> Optional[Field]:
|
def _get_current_field_from_assignment(ctx: FunctionContext, django_context: DjangoContext) -> Optional[Field]:
|
||||||
outer_model_info = helpers.get_typechecker_api(ctx).scope.active_class()
|
outer_model_info = helpers.get_typechecker_api(ctx).scope.active_class()
|
||||||
if (outer_model_info is None
|
if outer_model_info is None or not helpers.is_model_subclass_info(outer_model_info, django_context):
|
||||||
or not helpers.is_model_subclass_info(outer_model_info, django_context)):
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
field_name = None
|
field_name = None
|
||||||
@@ -60,8 +59,7 @@ def fill_descriptor_types_for_related_field(ctx: FunctionContext, django_context
|
|||||||
|
|
||||||
# self reference with abstract=True on the model where ForeignKey is defined
|
# self reference with abstract=True on the model where ForeignKey is defined
|
||||||
current_model_cls = current_field.model
|
current_model_cls = current_field.model
|
||||||
if (current_model_cls._meta.abstract
|
if current_model_cls._meta.abstract and current_model_cls == related_model_cls:
|
||||||
and current_model_cls == related_model_cls):
|
|
||||||
# for all derived non-abstract classes, set variable with this name to
|
# for all derived non-abstract classes, set variable with this name to
|
||||||
# __get__/__set__ of ForeignKey of derived model
|
# __get__/__set__ of ForeignKey of derived model
|
||||||
for model_cls in django_context.all_registered_model_classes:
|
for model_cls in django_context.all_registered_model_classes:
|
||||||
@@ -69,11 +67,10 @@ def fill_descriptor_types_for_related_field(ctx: FunctionContext, django_context
|
|||||||
derived_model_info = helpers.lookup_class_typeinfo(helpers.get_typechecker_api(ctx), model_cls)
|
derived_model_info = helpers.lookup_class_typeinfo(helpers.get_typechecker_api(ctx), model_cls)
|
||||||
if derived_model_info is not None:
|
if derived_model_info is not None:
|
||||||
fk_ref_type = Instance(derived_model_info, [])
|
fk_ref_type = Instance(derived_model_info, [])
|
||||||
derived_fk_type = reparametrize_related_field_type(default_related_field_type,
|
derived_fk_type = reparametrize_related_field_type(
|
||||||
set_type=fk_ref_type, get_type=fk_ref_type)
|
default_related_field_type, set_type=fk_ref_type, get_type=fk_ref_type
|
||||||
helpers.add_new_sym_for_info(derived_model_info,
|
)
|
||||||
name=current_field.name,
|
helpers.add_new_sym_for_info(derived_model_info, name=current_field.name, sym_type=derived_fk_type)
|
||||||
sym_type=derived_fk_type)
|
|
||||||
|
|
||||||
related_model = related_model_cls
|
related_model = related_model_cls
|
||||||
related_model_to_set = related_model_cls
|
related_model_to_set = related_model_cls
|
||||||
@@ -97,16 +94,14 @@ def fill_descriptor_types_for_related_field(ctx: FunctionContext, django_context
|
|||||||
related_model_to_set_type = Instance(related_model_to_set_info, []) # type: ignore
|
related_model_to_set_type = Instance(related_model_to_set_info, []) # type: ignore
|
||||||
|
|
||||||
# replace Any with referred_to_type
|
# replace Any with referred_to_type
|
||||||
return reparametrize_related_field_type(default_related_field_type,
|
return reparametrize_related_field_type(
|
||||||
set_type=related_model_to_set_type,
|
default_related_field_type, set_type=related_model_to_set_type, get_type=related_model_type
|
||||||
get_type=related_model_type)
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_field_descriptor_types(field_info: TypeInfo, is_nullable: bool) -> Tuple[MypyType, MypyType]:
|
def get_field_descriptor_types(field_info: TypeInfo, is_nullable: bool) -> Tuple[MypyType, MypyType]:
|
||||||
set_type = helpers.get_private_descriptor_type(field_info, '_pyi_private_set_type',
|
set_type = helpers.get_private_descriptor_type(field_info, "_pyi_private_set_type", is_nullable=is_nullable)
|
||||||
is_nullable=is_nullable)
|
get_type = helpers.get_private_descriptor_type(field_info, "_pyi_private_get_type", is_nullable=is_nullable)
|
||||||
get_type = helpers.get_private_descriptor_type(field_info, '_pyi_private_get_type',
|
|
||||||
is_nullable=is_nullable)
|
|
||||||
return set_type, get_type
|
return set_type, get_type
|
||||||
|
|
||||||
|
|
||||||
@@ -114,7 +109,7 @@ def set_descriptor_types_for_field(ctx: FunctionContext) -> Instance:
|
|||||||
default_return_type = cast(Instance, ctx.default_return_type)
|
default_return_type = cast(Instance, ctx.default_return_type)
|
||||||
|
|
||||||
is_nullable = False
|
is_nullable = False
|
||||||
null_expr = helpers.get_call_argument_by_name(ctx, 'null')
|
null_expr = helpers.get_call_argument_by_name(ctx, "null")
|
||||||
if null_expr is not None:
|
if null_expr is not None:
|
||||||
is_nullable = helpers.parse_bool(null_expr) or False
|
is_nullable = helpers.parse_bool(null_expr) or False
|
||||||
|
|
||||||
@@ -125,7 +120,7 @@ def set_descriptor_types_for_field(ctx: FunctionContext) -> Instance:
|
|||||||
def determine_type_of_array_field(ctx: FunctionContext, django_context: DjangoContext) -> MypyType:
|
def determine_type_of_array_field(ctx: FunctionContext, django_context: DjangoContext) -> MypyType:
|
||||||
default_return_type = set_descriptor_types_for_field(ctx)
|
default_return_type = set_descriptor_types_for_field(ctx)
|
||||||
|
|
||||||
base_field_arg_type = helpers.get_call_argument_type_by_name(ctx, 'base_field')
|
base_field_arg_type = helpers.get_call_argument_type_by_name(ctx, "base_field")
|
||||||
if not base_field_arg_type or not isinstance(base_field_arg_type, Instance):
|
if not base_field_arg_type or not isinstance(base_field_arg_type, Instance):
|
||||||
return default_return_type
|
return default_return_type
|
||||||
|
|
||||||
@@ -142,8 +137,7 @@ def transform_into_proper_return_type(ctx: FunctionContext, django_context: Djan
|
|||||||
assert isinstance(default_return_type, Instance)
|
assert isinstance(default_return_type, Instance)
|
||||||
|
|
||||||
outer_model_info = helpers.get_typechecker_api(ctx).scope.active_class()
|
outer_model_info = helpers.get_typechecker_api(ctx).scope.active_class()
|
||||||
if (outer_model_info is None
|
if outer_model_info is None or not helpers.is_model_subclass_info(outer_model_info, django_context):
|
||||||
or not helpers.is_model_subclass_info(outer_model_info, django_context)):
|
|
||||||
return ctx.default_return_type
|
return ctx.default_return_type
|
||||||
|
|
||||||
assert isinstance(outer_model_info, TypeInfo)
|
assert isinstance(outer_model_info, TypeInfo)
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ def make_meta_nested_class_inherit_from_any(ctx: ClassDefContext) -> None:
|
|||||||
|
|
||||||
|
|
||||||
def get_specified_form_class(object_type: Instance) -> Optional[TypeType]:
|
def get_specified_form_class(object_type: Instance) -> Optional[TypeType]:
|
||||||
form_class_sym = object_type.type.get('form_class')
|
form_class_sym = object_type.type.get("form_class")
|
||||||
if form_class_sym and isinstance(form_class_sym.type, CallableType):
|
if form_class_sym and isinstance(form_class_sym.type, CallableType):
|
||||||
return TypeType(form_class_sym.type.ret_type)
|
return TypeType(form_class_sym.type.ret_type)
|
||||||
return None
|
return None
|
||||||
@@ -28,7 +28,7 @@ def extract_proper_type_for_get_form(ctx: MethodContext) -> MypyType:
|
|||||||
object_type = ctx.type
|
object_type = ctx.type
|
||||||
assert isinstance(object_type, Instance)
|
assert isinstance(object_type, Instance)
|
||||||
|
|
||||||
form_class_type = helpers.get_call_argument_type_by_name(ctx, 'form_class')
|
form_class_type = helpers.get_call_argument_type_by_name(ctx, "form_class")
|
||||||
if form_class_type is None or isinstance(form_class_type, NoneTyp):
|
if form_class_type is None or isinstance(form_class_type, NoneTyp):
|
||||||
form_class_type = get_specified_form_class(object_type)
|
form_class_type = get_specified_form_class(object_type)
|
||||||
|
|
||||||
|
|||||||
@@ -9,13 +9,14 @@ from mypy_django_plugin.django.context import DjangoContext
|
|||||||
from mypy_django_plugin.lib import helpers
|
from mypy_django_plugin.lib import helpers
|
||||||
|
|
||||||
|
|
||||||
def get_actual_types(ctx: Union[MethodContext, FunctionContext],
|
def get_actual_types(
|
||||||
expected_keys: List[str]) -> List[Tuple[str, MypyType]]:
|
ctx: Union[MethodContext, FunctionContext], expected_keys: List[str]
|
||||||
|
) -> List[Tuple[str, MypyType]]:
|
||||||
actual_types = []
|
actual_types = []
|
||||||
# positionals
|
# positionals
|
||||||
for pos, (actual_name, actual_type) in enumerate(zip(ctx.arg_names[0], ctx.arg_types[0])):
|
for pos, (actual_name, actual_type) in enumerate(zip(ctx.arg_names[0], ctx.arg_types[0])):
|
||||||
if actual_name is None:
|
if actual_name is None:
|
||||||
if ctx.callee_arg_names[0] == 'kwargs':
|
if ctx.callee_arg_names[0] == "kwargs":
|
||||||
# unpacked dict as kwargs is not supported
|
# unpacked dict as kwargs is not supported
|
||||||
continue
|
continue
|
||||||
actual_name = expected_keys[pos]
|
actual_name = expected_keys[pos]
|
||||||
@@ -30,23 +31,23 @@ def get_actual_types(ctx: Union[MethodContext, FunctionContext],
|
|||||||
return actual_types
|
return actual_types
|
||||||
|
|
||||||
|
|
||||||
def typecheck_model_method(ctx: Union[FunctionContext, MethodContext], django_context: DjangoContext,
|
def typecheck_model_method(
|
||||||
model_cls: Type[Model], method: str) -> MypyType:
|
ctx: Union[FunctionContext, MethodContext], django_context: DjangoContext, model_cls: Type[Model], method: str
|
||||||
|
) -> MypyType:
|
||||||
typechecker_api = helpers.get_typechecker_api(ctx)
|
typechecker_api = helpers.get_typechecker_api(ctx)
|
||||||
expected_types = django_context.get_expected_types(typechecker_api, model_cls, method=method)
|
expected_types = django_context.get_expected_types(typechecker_api, model_cls, method=method)
|
||||||
expected_keys = [key for key in expected_types.keys() if key != 'pk']
|
expected_keys = [key for key in expected_types.keys() if key != "pk"]
|
||||||
|
|
||||||
for actual_name, actual_type in get_actual_types(ctx, expected_keys):
|
for actual_name, actual_type in get_actual_types(ctx, expected_keys):
|
||||||
if actual_name not in expected_types:
|
if actual_name not in expected_types:
|
||||||
ctx.api.fail('Unexpected attribute "{}" for model "{}"'.format(actual_name,
|
ctx.api.fail(f'Unexpected attribute "{actual_name}" for model "{model_cls.__name__}"', ctx.context)
|
||||||
model_cls.__name__),
|
|
||||||
ctx.context)
|
|
||||||
continue
|
continue
|
||||||
helpers.check_types_compatible(ctx,
|
helpers.check_types_compatible(
|
||||||
|
ctx,
|
||||||
expected_type=expected_types[actual_name],
|
expected_type=expected_types[actual_name],
|
||||||
actual_type=actual_type,
|
actual_type=actual_type,
|
||||||
error_message='Incompatible type for "{}" of "{}"'.format(actual_name,
|
error_message=f'Incompatible type for "{actual_name}" of "{model_cls.__name__}"',
|
||||||
model_cls.__name__))
|
)
|
||||||
|
|
||||||
return ctx.default_return_type
|
return ctx.default_return_type
|
||||||
|
|
||||||
@@ -59,7 +60,7 @@ def redefine_and_typecheck_model_init(ctx: FunctionContext, django_context: Djan
|
|||||||
if model_cls is None:
|
if model_cls is None:
|
||||||
return ctx.default_return_type
|
return ctx.default_return_type
|
||||||
|
|
||||||
return typecheck_model_method(ctx, django_context, model_cls, '__init__')
|
return typecheck_model_method(ctx, django_context, model_cls, "__init__")
|
||||||
|
|
||||||
|
|
||||||
def redefine_and_typecheck_model_create(ctx: MethodContext, django_context: DjangoContext) -> MypyType:
|
def redefine_and_typecheck_model_create(ctx: MethodContext, django_context: DjangoContext) -> MypyType:
|
||||||
@@ -72,4 +73,4 @@ def redefine_and_typecheck_model_create(ctx: MethodContext, django_context: Djan
|
|||||||
if model_cls is None:
|
if model_cls is None:
|
||||||
return ctx.default_return_type
|
return ctx.default_return_type
|
||||||
|
|
||||||
return typecheck_model_method(ctx, django_context, model_cls, 'create')
|
return typecheck_model_method(ctx, django_context, model_cls, "create")
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
from mypy.nodes import (
|
from mypy.nodes import GDEF, FuncDef, MemberExpr, NameExpr, RefExpr, StrExpr, SymbolTableNode, TypeInfo
|
||||||
GDEF, FuncDef, MemberExpr, NameExpr, RefExpr, StrExpr, SymbolTableNode, TypeInfo,
|
|
||||||
)
|
|
||||||
from mypy.plugin import ClassDefContext, DynamicClassDefContext
|
from mypy.plugin import ClassDefContext, DynamicClassDefContext
|
||||||
from mypy.types import AnyType, Instance, TypeOfAny
|
from mypy.types import AnyType, Instance, TypeOfAny
|
||||||
|
|
||||||
@@ -21,16 +19,15 @@ def create_new_manager_class_from_from_queryset_method(ctx: DynamicClassDefConte
|
|||||||
return
|
return
|
||||||
|
|
||||||
assert isinstance(base_manager_info, TypeInfo)
|
assert isinstance(base_manager_info, TypeInfo)
|
||||||
new_manager_info = semanal_api.basic_new_typeinfo(ctx.name,
|
new_manager_info = semanal_api.basic_new_typeinfo(
|
||||||
basetype_or_fallback=Instance(base_manager_info,
|
ctx.name, basetype_or_fallback=Instance(base_manager_info, [AnyType(TypeOfAny.unannotated)])
|
||||||
[AnyType(TypeOfAny.unannotated)]))
|
)
|
||||||
new_manager_info.line = ctx.call.line
|
new_manager_info.line = ctx.call.line
|
||||||
new_manager_info.defn.line = ctx.call.line
|
new_manager_info.defn.line = ctx.call.line
|
||||||
new_manager_info.metaclass_type = new_manager_info.calculate_metaclass_type()
|
new_manager_info.metaclass_type = new_manager_info.calculate_metaclass_type()
|
||||||
|
|
||||||
current_module = semanal_api.cur_mod_node
|
current_module = semanal_api.cur_mod_node
|
||||||
current_module.names[ctx.name] = SymbolTableNode(GDEF, new_manager_info,
|
current_module.names[ctx.name] = SymbolTableNode(GDEF, new_manager_info, plugin_generated=True)
|
||||||
plugin_generated=True)
|
|
||||||
passed_queryset = ctx.call.args[0]
|
passed_queryset = ctx.call.args[0]
|
||||||
assert isinstance(passed_queryset, NameExpr)
|
assert isinstance(passed_queryset, NameExpr)
|
||||||
|
|
||||||
@@ -55,15 +52,14 @@ def create_new_manager_class_from_from_queryset_method(ctx: DynamicClassDefConte
|
|||||||
assert isinstance(expr, StrExpr)
|
assert isinstance(expr, StrExpr)
|
||||||
custom_manager_generated_name = expr.value
|
custom_manager_generated_name = expr.value
|
||||||
else:
|
else:
|
||||||
custom_manager_generated_name = base_manager_info.name + 'From' + derived_queryset_info.name
|
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])
|
custom_manager_generated_fullname = ".".join(["django.db.models.manager", custom_manager_generated_name])
|
||||||
if 'from_queryset_managers' not in base_manager_info.metadata:
|
if "from_queryset_managers" not in base_manager_info.metadata:
|
||||||
base_manager_info.metadata['from_queryset_managers'] = {}
|
base_manager_info.metadata["from_queryset_managers"] = {}
|
||||||
base_manager_info.metadata['from_queryset_managers'][custom_manager_generated_fullname] = new_manager_info.fullname
|
base_manager_info.metadata["from_queryset_managers"][custom_manager_generated_fullname] = new_manager_info.fullname
|
||||||
|
|
||||||
class_def_context = ClassDefContext(cls=new_manager_info.defn,
|
class_def_context = ClassDefContext(cls=new_manager_info.defn, reason=ctx.call, api=semanal_api)
|
||||||
reason=ctx.call, api=semanal_api)
|
|
||||||
self_type = Instance(new_manager_info, [])
|
self_type = Instance(new_manager_info, [])
|
||||||
# we need to copy all methods in MRO before django.db.models.query.QuerySet
|
# we need to copy all methods in MRO before django.db.models.query.QuerySet
|
||||||
for class_mro_info in derived_queryset_info.mro:
|
for class_mro_info in derived_queryset_info.mro:
|
||||||
@@ -71,7 +67,6 @@ def create_new_manager_class_from_from_queryset_method(ctx: DynamicClassDefConte
|
|||||||
break
|
break
|
||||||
for name, sym in class_mro_info.names.items():
|
for name, sym in class_mro_info.names.items():
|
||||||
if isinstance(sym.node, FuncDef):
|
if isinstance(sym.node, FuncDef):
|
||||||
helpers.copy_method_to_another_class(class_def_context,
|
helpers.copy_method_to_another_class(
|
||||||
self_type,
|
class_def_context, self_type, new_method_name=name, method_node=sym.node
|
||||||
new_method_name=name,
|
)
|
||||||
method_node=sym.node)
|
|
||||||
|
|||||||
@@ -9,8 +9,7 @@ from mypy_django_plugin.lib import helpers
|
|||||||
|
|
||||||
|
|
||||||
def _get_field_instance(ctx: MethodContext, field_fullname: str) -> MypyType:
|
def _get_field_instance(ctx: MethodContext, field_fullname: str) -> MypyType:
|
||||||
field_info = helpers.lookup_fully_qualified_typeinfo(helpers.get_typechecker_api(ctx),
|
field_info = helpers.lookup_fully_qualified_typeinfo(helpers.get_typechecker_api(ctx), field_fullname)
|
||||||
field_fullname)
|
|
||||||
if field_info is None:
|
if field_info is None:
|
||||||
return AnyType(TypeOfAny.unannotated)
|
return AnyType(TypeOfAny.unannotated)
|
||||||
return Instance(field_info, [AnyType(TypeOfAny.explicit), AnyType(TypeOfAny.explicit)])
|
return Instance(field_info, [AnyType(TypeOfAny.explicit), AnyType(TypeOfAny.explicit)])
|
||||||
@@ -32,7 +31,7 @@ def return_proper_field_type_from_get_field(ctx: MethodContext, django_context:
|
|||||||
if model_cls is None:
|
if model_cls is None:
|
||||||
return ctx.default_return_type
|
return ctx.default_return_type
|
||||||
|
|
||||||
field_name_expr = helpers.get_call_argument_by_name(ctx, 'field_name')
|
field_name_expr = helpers.get_call_argument_by_name(ctx, "field_name")
|
||||||
if field_name_expr is None:
|
if field_name_expr is None:
|
||||||
return ctx.default_return_type
|
return ctx.default_return_type
|
||||||
|
|
||||||
|
|||||||
@@ -3,11 +3,9 @@ from typing import Dict, List, Optional, Type, cast
|
|||||||
from django.db.models.base import Model
|
from django.db.models.base import Model
|
||||||
from django.db.models.fields import DateField, DateTimeField
|
from django.db.models.fields import DateField, DateTimeField
|
||||||
from django.db.models.fields.related import ForeignKey
|
from django.db.models.fields.related import ForeignKey
|
||||||
from django.db.models.fields.reverse_related import (
|
from django.db.models.fields.reverse_related import ManyToManyRel, ManyToOneRel, OneToOneRel
|
||||||
ManyToManyRel, ManyToOneRel, OneToOneRel,
|
|
||||||
)
|
|
||||||
from mypy.nodes import ARG_STAR2, Argument, Context, FuncDef, TypeInfo, Var
|
from mypy.nodes import ARG_STAR2, Argument, Context, FuncDef, TypeInfo, Var
|
||||||
from mypy.plugin import ClassDefContext
|
from mypy.plugin import AttributeContext, ClassDefContext
|
||||||
from mypy.plugins import common
|
from mypy.plugins import common
|
||||||
from mypy.semanal import SemanticAnalyzer
|
from mypy.semanal import SemanticAnalyzer
|
||||||
from mypy.types import AnyType, Instance
|
from mypy.types import AnyType, Instance
|
||||||
@@ -35,7 +33,7 @@ class ModelClassInitializer:
|
|||||||
def lookup_typeinfo_or_incomplete_defn_error(self, fullname: str) -> TypeInfo:
|
def lookup_typeinfo_or_incomplete_defn_error(self, fullname: str) -> TypeInfo:
|
||||||
info = self.lookup_typeinfo(fullname)
|
info = self.lookup_typeinfo(fullname)
|
||||||
if info is None:
|
if info is None:
|
||||||
raise helpers.IncompleteDefnException(f'No {fullname!r} found')
|
raise helpers.IncompleteDefnException(f"No {fullname!r} found")
|
||||||
return info
|
return info
|
||||||
|
|
||||||
def lookup_class_typeinfo_or_incomplete_defn_error(self, klass: type) -> TypeInfo:
|
def lookup_class_typeinfo_or_incomplete_defn_error(self, klass: type) -> TypeInfo:
|
||||||
@@ -48,20 +46,17 @@ class ModelClassInitializer:
|
|||||||
var = Var(name=name, type=typ)
|
var = Var(name=name, type=typ)
|
||||||
# var.info: type of the object variable is bound to
|
# var.info: type of the object variable is bound to
|
||||||
var.info = self.model_classdef.info
|
var.info = self.model_classdef.info
|
||||||
var._fullname = self.model_classdef.info.fullname + '.' + name
|
var._fullname = self.model_classdef.info.fullname + "." + name
|
||||||
var.is_initialized_in_class = True
|
var.is_initialized_in_class = True
|
||||||
var.is_inferred = True
|
var.is_inferred = True
|
||||||
return var
|
return var
|
||||||
|
|
||||||
def add_new_node_to_model_class(self, name: str, typ: MypyType) -> None:
|
def add_new_node_to_model_class(self, name: str, typ: MypyType) -> None:
|
||||||
helpers.add_new_sym_for_info(self.model_classdef.info,
|
helpers.add_new_sym_for_info(self.model_classdef.info, name=name, sym_type=typ)
|
||||||
name=name,
|
|
||||||
sym_type=typ)
|
|
||||||
|
|
||||||
def add_new_class_for_current_module(self, name: str, bases: List[Instance]) -> TypeInfo:
|
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]
|
current_module = self.api.modules[self.model_classdef.info.module_name]
|
||||||
new_class_info = helpers.add_new_class_for_module(current_module,
|
new_class_info = helpers.add_new_class_for_module(current_module, name=name, bases=bases)
|
||||||
name=name, bases=bases)
|
|
||||||
return new_class_info
|
return new_class_info
|
||||||
|
|
||||||
def run(self) -> None:
|
def run(self) -> None:
|
||||||
@@ -103,8 +98,7 @@ class AddDefaultPrimaryKey(ModelClassInitializer):
|
|||||||
auto_field_info = self.lookup_typeinfo_or_incomplete_defn_error(auto_field_fullname)
|
auto_field_info = self.lookup_typeinfo_or_incomplete_defn_error(auto_field_fullname)
|
||||||
|
|
||||||
set_type, get_type = fields.get_field_descriptor_types(auto_field_info, is_nullable=False)
|
set_type, get_type = fields.get_field_descriptor_types(auto_field_info, is_nullable=False)
|
||||||
self.add_new_node_to_model_class(auto_field.attname, Instance(auto_field_info,
|
self.add_new_node_to_model_class(auto_field.attname, Instance(auto_field_info, [set_type, get_type]))
|
||||||
[set_type, get_type]))
|
|
||||||
|
|
||||||
|
|
||||||
class AddRelatedModelsId(ModelClassInitializer):
|
class AddRelatedModelsId(ModelClassInitializer):
|
||||||
@@ -117,11 +111,11 @@ class AddRelatedModelsId(ModelClassInitializer):
|
|||||||
field_sym = self.ctx.cls.info.get(field.name)
|
field_sym = self.ctx.cls.info.get(field.name)
|
||||||
if field_sym is not None and field_sym.node is not None:
|
if field_sym is not None and field_sym.node is not None:
|
||||||
error_context = field_sym.node
|
error_context = field_sym.node
|
||||||
self.api.fail(f'Cannot find model {field.related_model!r} '
|
self.api.fail(
|
||||||
f'referenced in field {field.name!r} ',
|
f"Cannot find model {field.related_model!r} " f"referenced in field {field.name!r} ",
|
||||||
ctx=error_context)
|
ctx=error_context,
|
||||||
self.add_new_node_to_model_class(field.attname,
|
)
|
||||||
AnyType(TypeOfAny.explicit))
|
self.add_new_node_to_model_class(field.attname, AnyType(TypeOfAny.explicit))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if related_model_cls._meta.abstract:
|
if related_model_cls._meta.abstract:
|
||||||
@@ -138,8 +132,7 @@ class AddRelatedModelsId(ModelClassInitializer):
|
|||||||
|
|
||||||
is_nullable = self.django_context.get_field_nullability(field, None)
|
is_nullable = self.django_context.get_field_nullability(field, None)
|
||||||
set_type, get_type = get_field_descriptor_types(field_info, is_nullable)
|
set_type, get_type = get_field_descriptor_types(field_info, is_nullable)
|
||||||
self.add_new_node_to_model_class(field.attname,
|
self.add_new_node_to_model_class(field.attname, Instance(field_info, [set_type, get_type]))
|
||||||
Instance(field_info, [set_type, get_type]))
|
|
||||||
|
|
||||||
|
|
||||||
class AddManagers(ModelClassInitializer):
|
class AddManagers(ModelClassInitializer):
|
||||||
@@ -154,10 +147,9 @@ class AddManagers(ModelClassInitializer):
|
|||||||
|
|
||||||
def get_generated_manager_mappings(self, base_manager_fullname: str) -> Dict[str, str]:
|
def get_generated_manager_mappings(self, base_manager_fullname: str) -> Dict[str, str]:
|
||||||
base_manager_info = self.lookup_typeinfo(base_manager_fullname)
|
base_manager_info = self.lookup_typeinfo(base_manager_fullname)
|
||||||
if (base_manager_info is None
|
if base_manager_info is None or "from_queryset_managers" not in base_manager_info.metadata:
|
||||||
or 'from_queryset_managers' not in base_manager_info.metadata):
|
|
||||||
return {}
|
return {}
|
||||||
return base_manager_info.metadata['from_queryset_managers']
|
return base_manager_info.metadata["from_queryset_managers"]
|
||||||
|
|
||||||
def create_new_model_parametrized_manager(self, name: str, base_manager_info: TypeInfo) -> Instance:
|
def create_new_model_parametrized_manager(self, name: str, base_manager_info: TypeInfo) -> Instance:
|
||||||
bases = []
|
bases = []
|
||||||
@@ -166,31 +158,27 @@ class AddManagers(ModelClassInitializer):
|
|||||||
if original_base.type is None:
|
if original_base.type is None:
|
||||||
raise helpers.IncompleteDefnException()
|
raise helpers.IncompleteDefnException()
|
||||||
|
|
||||||
original_base = helpers.reparametrize_instance(original_base,
|
original_base = helpers.reparametrize_instance(original_base, [Instance(self.model_classdef.info, [])])
|
||||||
[Instance(self.model_classdef.info, [])])
|
|
||||||
bases.append(original_base)
|
bases.append(original_base)
|
||||||
|
|
||||||
new_manager_info = self.add_new_class_for_current_module(name, bases)
|
new_manager_info = self.add_new_class_for_current_module(name, bases)
|
||||||
# copy fields to a new manager
|
# copy fields to a new manager
|
||||||
new_cls_def_context = ClassDefContext(cls=new_manager_info.defn,
|
new_cls_def_context = ClassDefContext(cls=new_manager_info.defn, reason=self.ctx.reason, api=self.api)
|
||||||
reason=self.ctx.reason,
|
|
||||||
api=self.api)
|
|
||||||
custom_manager_type = Instance(new_manager_info, [Instance(self.model_classdef.info, [])])
|
custom_manager_type = Instance(new_manager_info, [Instance(self.model_classdef.info, [])])
|
||||||
|
|
||||||
for name, sym in base_manager_info.names.items():
|
for name, sym in base_manager_info.names.items():
|
||||||
# replace self type with new class, if copying method
|
# replace self type with new class, if copying method
|
||||||
if isinstance(sym.node, FuncDef):
|
if isinstance(sym.node, FuncDef):
|
||||||
helpers.copy_method_to_another_class(new_cls_def_context,
|
helpers.copy_method_to_another_class(
|
||||||
self_type=custom_manager_type,
|
new_cls_def_context, self_type=custom_manager_type, new_method_name=name, method_node=sym.node
|
||||||
new_method_name=name,
|
)
|
||||||
method_node=sym.node)
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
new_sym = sym.copy()
|
new_sym = sym.copy()
|
||||||
if isinstance(new_sym.node, Var):
|
if isinstance(new_sym.node, Var):
|
||||||
new_var = Var(name, type=sym.type)
|
new_var = Var(name, type=sym.type)
|
||||||
new_var.info = new_manager_info
|
new_var.info = new_manager_info
|
||||||
new_var._fullname = new_manager_info.fullname + '.' + name
|
new_var._fullname = new_manager_info.fullname + "." + name
|
||||||
new_sym.node = new_var
|
new_sym.node = new_var
|
||||||
new_manager_info.names[name] = new_sym
|
new_manager_info.names[name] = new_sym
|
||||||
|
|
||||||
@@ -215,7 +203,7 @@ class AddManagers(ModelClassInitializer):
|
|||||||
manager_info = self.lookup_typeinfo(real_manager_fullname) # type: ignore
|
manager_info = self.lookup_typeinfo(real_manager_fullname) # type: ignore
|
||||||
if manager_info is None:
|
if manager_info is None:
|
||||||
continue
|
continue
|
||||||
manager_class_name = real_manager_fullname.rsplit('.', maxsplit=1)[1]
|
manager_class_name = real_manager_fullname.rsplit(".", maxsplit=1)[1]
|
||||||
|
|
||||||
if manager_name not in self.model_classdef.info.names:
|
if manager_name not in self.model_classdef.info.names:
|
||||||
manager_type = Instance(manager_info, [Instance(self.model_classdef.info, [])])
|
manager_type = Instance(manager_info, [Instance(self.model_classdef.info, [])])
|
||||||
@@ -225,10 +213,11 @@ class AddManagers(ModelClassInitializer):
|
|||||||
if not self.has_any_parametrized_manager_as_base(manager_info):
|
if not self.has_any_parametrized_manager_as_base(manager_info):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
custom_model_manager_name = manager.model.__name__ + '_' + manager_class_name
|
custom_model_manager_name = manager.model.__name__ + "_" + manager_class_name
|
||||||
try:
|
try:
|
||||||
custom_manager_type = self.create_new_model_parametrized_manager(custom_model_manager_name,
|
custom_manager_type = self.create_new_model_parametrized_manager(
|
||||||
base_manager_info=manager_info)
|
custom_model_manager_name, base_manager_info=manager_info
|
||||||
|
)
|
||||||
except helpers.IncompleteDefnException:
|
except helpers.IncompleteDefnException:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@@ -238,11 +227,11 @@ class AddManagers(ModelClassInitializer):
|
|||||||
class AddDefaultManagerAttribute(ModelClassInitializer):
|
class AddDefaultManagerAttribute(ModelClassInitializer):
|
||||||
def run_with_model_cls(self, model_cls: Type[Model]) -> None:
|
def run_with_model_cls(self, model_cls: Type[Model]) -> None:
|
||||||
# add _default_manager
|
# add _default_manager
|
||||||
if '_default_manager' not in self.model_classdef.info.names:
|
if "_default_manager" not in self.model_classdef.info.names:
|
||||||
default_manager_fullname = helpers.get_class_fullname(model_cls._meta.default_manager.__class__)
|
default_manager_fullname = helpers.get_class_fullname(model_cls._meta.default_manager.__class__)
|
||||||
default_manager_info = self.lookup_typeinfo_or_incomplete_defn_error(default_manager_fullname)
|
default_manager_info = self.lookup_typeinfo_or_incomplete_defn_error(default_manager_fullname)
|
||||||
default_manager = Instance(default_manager_info, [Instance(self.model_classdef.info, [])])
|
default_manager = Instance(default_manager_info, [Instance(self.model_classdef.info, [])])
|
||||||
self.add_new_node_to_model_class('_default_manager', default_manager)
|
self.add_new_node_to_model_class("_default_manager", default_manager)
|
||||||
|
|
||||||
|
|
||||||
class AddRelatedManagers(ModelClassInitializer):
|
class AddRelatedManagers(ModelClassInitializer):
|
||||||
@@ -272,8 +261,10 @@ class AddRelatedManagers(ModelClassInitializer):
|
|||||||
|
|
||||||
if isinstance(relation, (ManyToOneRel, ManyToManyRel)):
|
if isinstance(relation, (ManyToOneRel, ManyToManyRel)):
|
||||||
try:
|
try:
|
||||||
related_manager_info = self.lookup_typeinfo_or_incomplete_defn_error(fullnames.RELATED_MANAGER_CLASS) # noqa: E501
|
related_manager_info = self.lookup_typeinfo_or_incomplete_defn_error(
|
||||||
if 'objects' not in related_model_info.names:
|
fullnames.RELATED_MANAGER_CLASS
|
||||||
|
) # noqa: E501
|
||||||
|
if "objects" not in related_model_info.names:
|
||||||
raise helpers.IncompleteDefnException()
|
raise helpers.IncompleteDefnException()
|
||||||
except helpers.IncompleteDefnException as exc:
|
except helpers.IncompleteDefnException as exc:
|
||||||
if not self.api.final_iteration:
|
if not self.api.final_iteration:
|
||||||
@@ -282,16 +273,17 @@ class AddRelatedManagers(ModelClassInitializer):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
# create new RelatedManager subclass
|
# create new RelatedManager subclass
|
||||||
parametrized_related_manager_type = Instance(related_manager_info,
|
parametrized_related_manager_type = Instance(related_manager_info, [Instance(related_model_info, [])])
|
||||||
[Instance(related_model_info, [])])
|
default_manager_type = related_model_info.names["objects"].type
|
||||||
default_manager_type = related_model_info.names['objects'].type
|
if (
|
||||||
if (default_manager_type is None
|
default_manager_type is None
|
||||||
or not isinstance(default_manager_type, Instance)
|
or not isinstance(default_manager_type, Instance)
|
||||||
or default_manager_type.type.fullname == fullnames.MANAGER_CLASS_FULLNAME):
|
or default_manager_type.type.fullname == fullnames.MANAGER_CLASS_FULLNAME
|
||||||
|
):
|
||||||
self.add_new_node_to_model_class(attname, parametrized_related_manager_type)
|
self.add_new_node_to_model_class(attname, parametrized_related_manager_type)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
name = related_model_cls.__name__ + '_' + 'RelatedManager'
|
name = related_model_cls.__name__ + "_" + "RelatedManager"
|
||||||
bases = [parametrized_related_manager_type, default_manager_type]
|
bases = [parametrized_related_manager_type, default_manager_type]
|
||||||
new_related_manager_info = self.add_new_class_for_current_module(name, bases)
|
new_related_manager_info = self.add_new_class_for_current_module(name, bases)
|
||||||
|
|
||||||
@@ -303,45 +295,50 @@ class AddExtraFieldMethods(ModelClassInitializer):
|
|||||||
# get_FOO_display for choices
|
# get_FOO_display for choices
|
||||||
for field in self.django_context.get_model_fields(model_cls):
|
for field in self.django_context.get_model_fields(model_cls):
|
||||||
if field.choices:
|
if field.choices:
|
||||||
info = self.lookup_typeinfo_or_incomplete_defn_error('builtins.str')
|
info = self.lookup_typeinfo_or_incomplete_defn_error("builtins.str")
|
||||||
return_type = Instance(info, [])
|
return_type = Instance(info, [])
|
||||||
common.add_method(self.ctx,
|
common.add_method(self.ctx, name=f"get_{field.attname}_display", args=[], return_type=return_type)
|
||||||
name='get_{}_display'.format(field.attname),
|
|
||||||
args=[],
|
|
||||||
return_type=return_type)
|
|
||||||
|
|
||||||
# get_next_by, get_previous_by for Date, DateTime
|
# get_next_by, get_previous_by for Date, DateTime
|
||||||
for field in self.django_context.get_model_fields(model_cls):
|
for field in self.django_context.get_model_fields(model_cls):
|
||||||
if isinstance(field, (DateField, DateTimeField)) and not field.null:
|
if isinstance(field, (DateField, DateTimeField)) and not field.null:
|
||||||
return_type = Instance(self.model_classdef.info, [])
|
return_type = Instance(self.model_classdef.info, [])
|
||||||
common.add_method(self.ctx,
|
common.add_method(
|
||||||
name='get_next_by_{}'.format(field.attname),
|
self.ctx,
|
||||||
args=[Argument(Var('kwargs', AnyType(TypeOfAny.explicit)),
|
name=f"get_next_by_{field.attname}",
|
||||||
|
args=[
|
||||||
|
Argument(
|
||||||
|
Var("kwargs", AnyType(TypeOfAny.explicit)),
|
||||||
AnyType(TypeOfAny.explicit),
|
AnyType(TypeOfAny.explicit),
|
||||||
initializer=None,
|
initializer=None,
|
||||||
kind=ARG_STAR2)],
|
kind=ARG_STAR2,
|
||||||
return_type=return_type)
|
)
|
||||||
common.add_method(self.ctx,
|
],
|
||||||
name='get_previous_by_{}'.format(field.attname),
|
return_type=return_type,
|
||||||
args=[Argument(Var('kwargs', AnyType(TypeOfAny.explicit)),
|
)
|
||||||
|
common.add_method(
|
||||||
|
self.ctx,
|
||||||
|
name=f"get_previous_by_{field.attname}",
|
||||||
|
args=[
|
||||||
|
Argument(
|
||||||
|
Var("kwargs", AnyType(TypeOfAny.explicit)),
|
||||||
AnyType(TypeOfAny.explicit),
|
AnyType(TypeOfAny.explicit),
|
||||||
initializer=None,
|
initializer=None,
|
||||||
kind=ARG_STAR2)],
|
kind=ARG_STAR2,
|
||||||
return_type=return_type)
|
)
|
||||||
|
],
|
||||||
|
return_type=return_type,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class AddMetaOptionsAttribute(ModelClassInitializer):
|
class AddMetaOptionsAttribute(ModelClassInitializer):
|
||||||
def run_with_model_cls(self, model_cls: Type[Model]) -> None:
|
def run_with_model_cls(self, model_cls: Type[Model]) -> None:
|
||||||
if '_meta' not in self.model_classdef.info.names:
|
if "_meta" not in self.model_classdef.info.names:
|
||||||
options_info = self.lookup_typeinfo_or_incomplete_defn_error(fullnames.OPTIONS_CLASS_FULLNAME)
|
options_info = self.lookup_typeinfo_or_incomplete_defn_error(fullnames.OPTIONS_CLASS_FULLNAME)
|
||||||
self.add_new_node_to_model_class('_meta',
|
self.add_new_node_to_model_class("_meta", Instance(options_info, [Instance(self.model_classdef.info, [])]))
|
||||||
Instance(options_info, [
|
|
||||||
Instance(self.model_classdef.info, [])
|
|
||||||
]))
|
|
||||||
|
|
||||||
|
|
||||||
def process_model_class(ctx: ClassDefContext,
|
def process_model_class(ctx: ClassDefContext, django_context: DjangoContext) -> None:
|
||||||
django_context: DjangoContext) -> None:
|
|
||||||
initializers = [
|
initializers = [
|
||||||
InjectAnyAsBaseForNestedMeta,
|
InjectAnyAsBaseForNestedMeta,
|
||||||
AddDefaultPrimaryKey,
|
AddDefaultPrimaryKey,
|
||||||
@@ -358,3 +355,9 @@ def process_model_class(ctx: ClassDefContext,
|
|||||||
except helpers.IncompleteDefnException:
|
except helpers.IncompleteDefnException:
|
||||||
if not ctx.api.final_iteration:
|
if not ctx.api.final_iteration:
|
||||||
ctx.api.defer()
|
ctx.api.defer()
|
||||||
|
|
||||||
|
|
||||||
|
def set_auth_user_model_boolean_fields(ctx: AttributeContext, django_context: DjangoContext) -> MypyType:
|
||||||
|
boolinfo = helpers.lookup_class_typeinfo(helpers.get_typechecker_api(ctx), bool)
|
||||||
|
assert boolinfo is not None
|
||||||
|
return Instance(boolinfo, [])
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user